java --- 设计模式 --- 动态代理
Java设计模式——动态代理
java提供了动态代理的对象,本文主要探究它的实现,
动态代理是AOP(面向切面编程, Aspect Oriented Programming)的基础实现方式,
动态代理使代码的重复更少,更便与维护
本文参考了满一行老师和马士兵老师的视频,在此表示Thanks。
假设小张通过QQ和一个网名为如花的妹纸聊天,
而在QQ的另一端,这个网名为如花的妹纸的name是小丽
但是,正在聊天的时候小丽生病了不想打字,小丽就找她的男朋友帮忙回应
小丽的男朋友在如花的QQ账号上与小张聊天,小张并不知道和他聊天的如花是谁
小张发来消息了,"HI, 你好,",小丽的男朋友说,“小张发来消息了‘HI, 你好’,我该怎么回?”
小丽说,那就回一个“你也好, 哈哈”吧, 小张的男朋友按照小丽说的做了。
这就是代理模式的雏形,小丽实现了如花这个接口,小张也实现了如花这个接口
小张调用的是如花这个接口,但是他并不知道,和他聊天的是小丽,还是小丽的男朋友
小丽的男朋友因为在信息的必经道上,所以可以对信息进行扭曲以及篡改,对不良的信息过滤
哈哈,这就是面向切面编程的一个例子了。
故事就讲到这里,下面来解释一下静态代理
Tank类实现一个Movable接口,怎样知道它的运行时间呢方法move()的运行时间呢
我们可以在方法前面和后面都记录时间, long start = System.currentTimeMillis();
/Proxy/src/yuki/design/proxy/package1/Movable.java
package yuki.design.proxy.package1;
public interface Movable {
void move();
}
/Proxy/src/yuki/design/proxy/package1/Tank.java
package yuki.design.proxy.package1;
import java.util.Random;
public class Tank implements Movable {
@Override
public void move() {
// long start = System.currentTimeMillis();
System.out.println("Tank is moving...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// long end = System.currentTimeMillis();
// System.out.println("time:" + (start-end));
}
}
通过注释掉的内容可以计算出夹在中间代码的运行时间
这种方法需要修改源码,但是很多方法都是不知道源码的class文件
这个方式拿到的也不是方法本身运行的时间,因为方法已经被篡改了
还有一种方法是要在main方法中把方法执行的前后加上时间的记录
这是不行的,因为jdk为方法的运行准备也需要时间,这显然是不精确的
哦,我们可以继承Tank这个类,重写它的方法就可以了
用继承把原来的方法前后加一些逻辑,原来的方法就用super调用
/Proxy/src/yuki/design/proxy/package1/Tank2.java
package yuki.design.proxy.package1;
public class Tank2 extends Tank {
@Override
public void move() {
long start = System.currentTimeMillis();
super.move();
long end = System.currentTimeMillis();
System.out.println("time:" + (start-end));
}
}
一个类里面有另一个类的对象,这种对象间的关系叫做聚合,
它也可以实现上面的继承所提到的效果,只是把父类改成代理类的一个成员变量而已
Tank3是用聚合的方式实现的
/Proxy/src/yuki/design/proxy/package1/Tank3.java
package yuki.design.proxy.package1;
public class Tank3 implements Movable {
public Tank3(Tank t) {
this.t = t;
}
Tank t;
@Override
public void move() {
long start = System.currentTimeMillis();
t.move();
long end = System.currentTimeMillis();
System.out.println("time:" + (start-end));
}
}
继承明显是不灵活的,也不符合日常的逻辑
一般来说,如果可能这个类有多个代理类呢,比如说,添加一个记录日志的类
功能上的叠加,先记录日志,后记录时间;或者是相反的顺序
如果用继承的方式,会导致类越来越多
如果用聚合的方式,可以代理Movable接口,这里的关键是实现同一接口
/Proxy/src/yuki/design/proxy/package1/TankTimeProxy.java
package yuki.design.proxy.package1;
public class TankTimeProxy implements Movable {
public TankTimeProxy(/*Tank*/Movable t) {
this.t = t;
}
/*Tank*/Movable t;
@Override
public void move() {
long start = System.currentTimeMillis();
t.move();
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
}
/Proxy/src/yuki/design/proxy/package1/TankLogProxy.java
package yuki.design.proxy.package1;
public class TankLogProxy implements Movable{
public TankLogProxy(/*Tank*/ Movable t) {
this.t = t;
}
/*Tank*/Movable t;
@Override
public void move() {
System.out.println("Tank start......");
t.move();
System.out.println("Tank stop......");
}
}
注意,这里代理类的对象并不是真实的对象,而是接口,通过这个接口
这两个代理类和被代理的对象就可以自由组合
严格的说,在被代理对象和最终的代理对象之间可以随机插入代理类
每一个代理类就可以看作是一个切片
/Proxy/src/yuki/design/proxy/package1/Client.java
package yuki.design.proxy.package1;
public class Client {
public static void main(String[] args) {
Tank t = new Tank();
TankTimeProxy timeProxy = new TankTimeProxy(t);
TankLogProxy logProxy = new TankLogProxy(timeProxy);
Movable m = logProxy;
m.move();
/*
Movable logProxy = new TankLogProxy(t);
Movable timeLogProxy = new TankTimeProxy(logProxy);
timeLogProxy.move();
*/
}
}
spring这个轻量级容器,提供了继承和聚合实现代理的方式,
但是,强烈建议我们使用聚合的哪一种
可以说,AOP是动态代理的一个应用
还要说明的是,当一个接口有多个方法时,相同的代码必须重复
就是封装成对象也至少要插入一条插入调用方法的语句
/Proxy/src/yuki/design/proxy/package2/Movable.java
package yuki.design.proxy.package2;
public interface Movable {
void move();
void stop();
}
/Proxy/src/yuki/design/proxy/package2/Tank.java
package yuki.design.proxy.package2;
import java.util.Random;
public class Tank implements Movable {
@Override
public void move() {
System.out.println("Tank is moving...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void stop() {
System.out.println("Tank is stopping...");
}
}
/Proxy/src/yuki/design/proxy/package2/TankTimeProxy.java
package yuki.design.proxy.package2;
public class TankTimeProxy implements Movable {
public TankTimeProxy(Movable t) {
this.t = t;
}
Movable t;
@Override
public void move() {
long start = System.currentTimeMillis();
t.move();
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
@Override
public void stop() {
long start = System.currentTimeMillis();
t.stop();
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
}
如果Movable接口里还有其它的方法,计算时间的代码在代理类中还得重写
可以把相同的代码放在方法中,before,after
怎样让计算时间的代理变得更加通用,
不只是计算坦克的时间,还可以计算汽车的
怎样写一个通用的时间代理
我们应该怎样生成一个代理呢
动态代理的意思是,不再看到代理类的名字,需要的是代理对象直接产生
加入能把源码编译完产生新的类,再把这个类放入内存,产生新的对象
怎样对这段代码动态的编译,动态编译完成就能产生动态代理了
现在看不到这个动态代理类的名字
这里可以自定义proxy的实现,而具体的类就不用写了
/Proxy/src/yuki/design/proxy/package3/Movable.java
/Proxy/src/yuki/design/proxy/package3/Tank.java
package yuki.design.proxy.package3;
import java.util.Random;
public class Tank implements Movable {
@Override
public void move() {
// long start = System.currentTimeMillis();
System.out.println("Tank is moving...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// long end = System.currentTimeMillis();
// System.out.println("time:" + (start-end));
}
}
package yuki.design.proxy.package3;
import java.util.Random;
public class Tank implements Movable {
@Override
public void move() {
// long start = System.currentTimeMillis();
System.out.println("Tank is moving...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// long end = System.currentTimeMillis();
// System.out.println("time:" + (start-end));
}
}
在JDK6里,提供了编译java代码的API
获取项目的根路径System.getProperty("user.dir");
通过FileWriter的write方法把字符串写到文件中
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler是空值,把/Java/Installed JREs由jre改为jdk
这里的jre还是从jdk下的jre下拿的
/JavaBuildPath/Libraries选择Workspace default JRE(jdk1.8.0)
在Eclipse中,必须先编译好,有了文件后才能加载类
使用反射加载对象,运行方法
/Proxy/src/yuki/design/proxy/Proxy.java
package yuki.design.proxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class Proxy {
public static Object newProxyInstance(Class<?> interfaces, InvocationHandler h) throws Exception {
String rt = "\r\n";
String methodStr = "";
Method[] methods = interfaces.getMethods();
/*for(Method m : methods){
methodStr +=
" @Override" + rt +
" public void "+ m.getName() +"(){" + rt +
" long start = System.currentTimeMillis();"+rt+
" System.out.println(\"starttime:\" + start);"+rt+
" t."+ m.getName() +"();"+rt+
" long end = System.currentTimeMillis();"+rt+
" System.out.println(\"time:\" + (end-start));"+rt+
" }";
}*/
for(Method m : methods){
methodStr +=
" @Override" + rt +
" public void "+ m.getName() +"() {" + rt +
" try{" + rt +
" Method md = "+ interfaces.getName() +".class.getMethod(\""+ m.getName() +"\");" + rt +
" h.invoke(this, md);"+rt+
" }catch(Exception e){ e.printStackTrace(); }" + rt +
" }";
}
/*String src =
"package yuki.design.proxy.package3;"+rt+
"public class TankTimeProxy implements "+ interfaces.getName() +" {"+rt+
" public TankTimeProxy("+ interfaces.getName() +" t) {"+rt+
" this.t = t;"+rt+
" }"+rt+
" "+ interfaces.getName() +" t;"+rt+
methodStr +rt+
"}";*/
String src =
"package yuki.design.proxy.package3;"+rt+
"import yuki.design.proxy.InvocationHandler;"+rt+
"import java.lang.reflect.Method;"+rt+
"public class TankTimeProxy implements "+ interfaces.getName() +" {"+rt+
" public TankTimeProxy(InvocationHandler h) {"+rt+
" this.h = h;"+rt+
" }"+rt+
" InvocationHandler h;"+rt+
methodStr +rt+
"}";
String fileName = System.getProperty("user.dir") +
"/temp/yuki/design/proxy/package3/TankTimeProxy.java";
File f = new File(fileName);
if(!f.exists()){ f.mkdirs(); f.delete();}
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
/*
* compile
*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// System.out.println(compiler); //com.sun.tools.javac.api.JavacTool@a570f
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> units = fileManager.getJavaFileObjects(fileName);
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
task.call();
fileManager.close();
/*
* load into memory and create an instance
*/
URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir") + "/temp/")};
//System.out.println(urls[0]); //file:/D:/Workspaces/Eclipse/Proxy/bin
URLClassLoader ul = new URLClassLoader(urls);
Class<?> c = ul.loadClass("yuki.design.proxy.package3.TankTimeProxy");
//System.out.println(c); //class yuki.design.proxy.package3.TankTimeProxy
ul.close();
Constructor<?> constructor = c.getConstructor(InvocationHandler.class);
Object m = constructor.newInstance(h);
return m;
}
}
现在的代理只能实现Movable接口,现在要求可以实现任意接口的
传入Class<?>类型的参数就可以了
站在ClassLoader的角度Method也是一系列的对象
假设有很多方法就遍历所有的方法
往里传入任意接口,就可以产生实现了这个接口的任意对象
更换为其它的目录后,一次运行就可以成功
原先在src目录下,会有两份class文件,
一份是eclipse编译的,一份是ClassLoader加载的
/Proxy/src/yuki/design/proxy/InvocationHandler.java
package yuki.design.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
void invoke(Object o, Method m);
}
这里,实现了InvocationHandler的invoke(Obejct, Method)方法
/Proxy/src/yuki/design/proxy/TimeHandler.java
package yuki.design.proxy;
import java.lang.reflect.Method;
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
long start = System.currentTimeMillis();
System.out.println("starttime:" + start);
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
}
我们来看一看它是如何被调用的吧
/Proxy/src/yuki/design/proxy/Client.java
package yuki.design.proxy;
import yuki.design.proxy.package3.Movable;
import yuki.design.proxy.package3.Tank;
public class Client {
public static void main(String[] args) throws Exception {
Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);
Movable m = (Movable) Proxy.newProxyInstance(Movable.class, h);
// Movable m = (Movable) Proxy.newProxyInstance(Comparable.class);
m.move();
}
}
看一看生成的代理java文件和class文件

打开TankTimeProxy.java
package yuki.design.proxy.package3;
import yuki.design.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class TankTimeProxy implements yuki.design.proxy.package3.Movable {
public TankTimeProxy(InvocationHandler h) {
this.h = h;
}
InvocationHandler h;
@Override
public void move() {
try{
Method md = yuki.design.proxy.package3.Movable.class.getMethod("move");
h.invoke(this, md);
}catch(Exception e){ e.printStackTrace(); }
}
}
它被翻译成了class文件,加载进内存,生成对象
if(!f.exists()){ f.mkdirs(); f.delete();}
这一句的意思是,如果文件不存在就建立这些文件,但是在文件的树梢
f.mkdirs()之后,有一个TimeTankProxy.java的文件夹,
导致在该目录下不能新建TimeTankProxy.java的文件,所以要f.delete();
运行看一下结果吧,控制台打印输出如下语句
starttime:1407701374031 Tank is moving... time:5934
补充一些小细节,
/Proxy/src/yuki/design/proxy/complier/Test1.java
package yuki.design.proxy.complier;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import yuki.design.proxy.package3.Movable;
import yuki.design.proxy.package3.Tank;
public class Test1 {
public static void main(String[] args) throws Exception {
String rt = "\r\n";
String src =
"package yuki.design.proxy.package3;"+rt+
"public class TankTimeProxy implements Movable {"+rt+
" public TankTimeProxy(Movable t) {"+rt+
" this.t = t;"+rt+
" }"+rt+
" Movable t;"+rt+
" @Override"+rt+
" public void move() {"+rt+
" long start = System.currentTimeMillis();"+rt+
" System.out.println(\"starttime:\" + start);"+rt+
" t.move();"+rt+
" long end = System.currentTimeMillis();"+rt+
" System.out.println(\"time:\" + (end-start));"+rt+
" }"+rt+
"}";
String fileName = System.getProperty("user.dir") +
"/src/yuki/design/proxy/package3/TankTimeProxy.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
/*
* compile
*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// System.out.println(compiler); //com.sun.tools.javac.api.JavacTool@a570f
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> units = fileManager.getJavaFileObjects(fileName);
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
task.call();
fileManager.close();
/*
* load into memory and create an instance
*/
URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir") + "/src")};
//System.out.println(urls[0]); //file:/D:/Workspaces/Eclipse/Proxy/bin
URLClassLoader ul = new URLClassLoader(urls);
Class<?> c = ul.loadClass("yuki.design.proxy.package3.TankTimeProxy");
//System.out.println(c); //class yuki.design.proxy.package3.TankTimeProxy
ul.close();
Constructor<?> constructor = c.getConstructor(Movable.class);
Movable m = (Movable) constructor.newInstance(new Tank());
m.move();
}
}
/Proxy/src/yuki/design/proxy/complier/Test2.java
package yuki.design.proxy.complier;
import java.lang.reflect.Method;
import yuki.design.proxy.package3.Movable;
public class Test2 {
public static void main(String[] args) {
Method[] methods = Movable.class.getMethods();
for(Method m : methods){
System.out.println(m.getName());
}
}
}
现在,我们一起来连贯一下代理的主要细节:
一个代理类TimeHandler实现一个接口InvocationHandler
接口的方法是invoke(Object, Method)
这个类需要传入一个被代理对象Tank,
并提供一个它的每个方法需要添加的代码,在invoke中调用Tank的方法
它会被Proxy的生成器吸收,遍历这个类的所有方法
最后会在一个临时的目录生成代码并编译,在生成的类中,
它吸收了InvocationHandler,
并通过invoke的方法参数调用被代理类的每个方法
生成Class文件,加载进内存,获得了这个代理类的对象
这时,调用这个代理类的每个方法都会执行在TimeHandler中被填入的内容
使用代理类实现不修改原来的代码,在代码前后添加逻辑
面向切面编程,这个逻辑是可插拔的,可以把逻辑卸载配置文件中
配置是可以叠加的,
既然动态代理的类已写好,我们让它来实现一些其它的代理逻辑
/Proxy/src/yuki/design/proxy/test/UserMgr.java
package yuki.design.proxy.test;
public interface UserMgr {
void addUser();
}
/Proxy/src/yuki/design/proxy/test/UserMgrImpl.java
package yuki.design.proxy.test;
public class UserMgrImpl implements UserMgr {
@Override
public void addUser() {
System.out.println("1.插入记录到user表");
System.out.println("2.记录日志到日志表");
}
}
/Proxy/src/yuki/design/proxy/test/TransactionHandler.java
package yuki.design.proxy.test;
import java.lang.reflect.Method;
import yuki.design.proxy.InvocationHandler;
public class TransactionHandler implements InvocationHandler {
private Object target;
public TransactionHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
System.out.println("Transaction start");
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Transaction commit");
}
}
/Proxy/src/yuki/design/proxy/test/Client.java
package yuki.design.proxy.test;
import java.lang.reflect.Method;
import yuki.design.proxy.InvocationHandler;
public class TransactionHandler implements InvocationHandler {
private Object target;
public TransactionHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
System.out.println("Transaction start");
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Transaction commit");
}
}
运行一下,就会看到结果,编译的文件以及控制台输出的结果
package yuki.design.proxy.package3;
import yuki.design.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class TankTimeProxy implements yuki.design.proxy.test.UserMgr {
public TankTimeProxy(InvocationHandler h) {
this.h = h;
}
InvocationHandler h;
@Override
public void addUser() {
try{
Method md = yuki.design.proxy.test.UserMgr.class.getMethod("addUser");
h.invoke(this, md);
}catch(Exception e){ e.printStackTrace(); }
}
}
starttime: Transaction start .插入记录到user表 .记录日志到日志表 Transaction commit time:
在最后用到了两个代理类的嵌套,它也可以用配置文件变成灵活的,即配置是可叠加的
jdk的动态代理有Proxy类的
newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)
接口InvocationHandler有方法
invoke(Object, Method, Object[])
简单的小思考:
什么叫动态代理?
动态代理是怎么产生的?
动态代理有什么用?
怎样使用JavaAPI生成动态代理呢
/Proxy/src/yuki/design/proxy/java/EmpService.java
package yuki.design.proxy.java;
public interface EmpService {
void save();
void update();
void delete(int id);
}
/Proxy/src/yuki/design/proxy/java/EmpServiceImpl.java
package yuki.design.proxy.java;
public class EmpServiceImpl implements EmpService {
public void save() {
System.out.println("EmpService save");
}
@Override
public void update() {
System.out.println("EmpService update");
}
@Override
public void delete(int id) {
System.out.println("EmpService delete, id="+ id);
}
}
/Proxy/src/yuki/design/proxy/java/EmpServiceProxy.java
package yuki.design.proxy.java;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class EmpServiceProxy {
public static void main(String[] args) {
//被代理的目标对象
final EmpService target = new EmpServiceImpl();
/**
* 用来产生代理的类
* 第一个参数:classLoader, 用来加载*.class文件,类加载器
* 需要拿到当前线程的类加载器
* 第二个参数:代理类应当实现的接口,可以实现多个接口
* 第三个参数:一个接口
*/
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = new Class[]{EmpService.class};
InvocationHandler h = new InvocationHandler() {
/**
* Object proxy 代理对象自己
* Method method 代理对象正在被调用的那个方法,是在接口里定义的
* Object[] args 方法的参数数组
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("方法名:" + method.getName());
try {
System.out.println("开始事物");
// 调用真正业务逻辑
System.out.println("===============");
method.invoke(target, args);
System.out.println("===============");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
} finally {
System.out.println("释放资源");
}
return null;
}
};
EmpService service = (EmpService) Proxy.newProxyInstance(loader, interfaces, h);
// service.save();
// service.update();
service.delete(111);
}
}
运行,会在控制台打印出如下语句
方法名:delete 开始事物 =============== EmpService delete, id=111 =============== 提交事务 释放资源
整个演示项目的包路径如下:

对于面向切面变成来说,有切入点和切面两个术语
于切入点匹配的类才会由spring生成动态代理,在spring中,我们需要写切入点表达式
切面是对应的附加操作,可以把这部分操作写在通知类中
<bean class="通知类"></bean>
<aop:config>
<aop:pointcut id="切入点id" expression="切入点表达式"></aop:pointcut>
<aop:aspect ref="通知类id">
<aop:通知类型 method="通知类中的方法" pointcut-ref="切入点id"></<aop:通知类型>
</aop:aspect>
</aop:config>
Object 代理对象 = context.getBean("目标对象id");
将主业务逻辑的附加操作抽取出去,根据功能不同,抽取为不同通知类的思想,称为面向切面编程
更多好文请查看:http://www.cnblogs.com/kodoyang/
孔东阳
2014/8/11
java --- 设计模式 --- 动态代理的更多相关文章
- JAVA设计模式-动态代理(Proxy)示例及说明
在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...
- JAVA设计模式-动态代理(Proxy)源码分析
在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...
- 【设计模式】Java设计模式 - 动态代理
[设计模式]Java设计模式 - 动态代理 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 最近工作比较忙,没啥时间学习 目录 [设计模 ...
- Java 反射 设计模式 动态代理机制详解 [ 转载 ]
Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...
- Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景
我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...
- java中动态代理实现机制
前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...
- [转载] java的动态代理机制详解
转载自http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代 ...
- java设计模式6——代理模式
java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代 ...
- java的动态代理机制详解
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
随机推荐
- C#AutoResetEvent和ManualResetEvent的区别
一:终止状态和非终止状态 首先说说线程的终止状态和非终止状态.AutoResetEvent和ManualResetEvent的构造函数中,都有bool变量来指明线程的终止状态和非终止状态.true表示 ...
- Maven实战(六)--- dependencies与dependencyManagement的区别
在上一个项目中遇到一些jar包冲突的问题,之后还有很多人分不清楚dependencies与dependencyManagement的区别,本篇文章将这些区别总结下来. 1.DepencyManagem ...
- SQL Server 联表字段合并查询
经常遇到统计报表中,子表记录合并为一个字段的情况.例如:省表中各省经济水平前五的城市统计. 有如下两表:dbo.省 和 dbo.市 (好吧,你可能会吐槽为什么用中文表名,其实我是为了方便查找替换) 这 ...
- 习惯&感恩
A不喜欢吃鸡蛋,都给了B吃,刚开始B很感谢,久而久之便习惯了.习惯了,便理所当然了. 后来有一天,A将鸡蛋给了C,B就不爽了.她忘记了这个鸡蛋本来就是A的,A想给谁都可以. 为此,她们大吵一架,从此绝 ...
- 使用jQuery插件PrintArea进行html页面打印
在开发系统时,我们一般会有统计分析的功能需求,而现在大多数系统都是使用B/S的形式开发. 所以,html报表打印,并且打印指定内容,成了必要的功能. 开源地址:https://github.com/R ...
- BigDecimal四舍五入
/*由数字字符串构造BigDecimal的方法 *设置BigDecimal的小数位数的方法 */ import java.math.BigDecimal; //数字字符串 String StrBd=& ...
- web.xml中webAppRootKey
------------------------------------------------------------------------------------------------1. w ...
- clientTop、clientWidth、offsetTop、offsetWidth、scrollTop
<div id="drag" class="drag">drag me</div> <script type="text ...
- 【同行说技术】Android图片处理技术资料汇总(一)
对于Android开发的童鞋们来说,图片处理时或多或少都会遇到令人头疼和不满意的问题,今天小编收集了5篇Android图片处理的干货文章,一起来看看吧! 一.Android 高清加载巨图方案 拒绝压缩 ...
- IOS中控制器的重要方法使用
1.屏幕即将旋转的时候调用(控制器监控屏幕旋转) - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfac ...