1、先简单使用InvocationHandler,Proxy来实现动态代理,目的:为Tank添加一个代理TimeProxy,由TimeProxy为Tank添加额外的计时功能(在不改变Tank原有特性前提下,使用动态代理再合适不过)
首先来看下需要被代理的对象Tank相关信息:
public interface Moveable {
public void move();
}
public class Tank implements Moveable{
@Override
public void move() {
System.out.println("坦克移动中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
用TimerHandler实现InvocationHandler接口,该类维护一个被代理对象target,实现InvocationHandler接口的invoke方法,通过反射调用target的指定方法:
public class TimerHandler implements InvocationHandler{
private Object target;
public TimerHandler(Object object){
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long start = System.currentTimeMillis();
System.out.println("start:"+start);
//通过反射调用 被代理对象tank的指定方法
method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("end:"+end);
System.out.println("total:"+(end-start));
return null;
}
}
测试代码:
public class ProxyTest {
public static void main(String[] args) throws Exception {
Moveable tank = new Tank();
//将被代理对象注入Invocationhandler
TimerHandler handler = new TimerHandler(tank);
//通过Proxy.newProxyInstance方法得到TankProxy
Moveable tankProxy = (Moveable)java.lang.reflect.Proxy.newProxyInstance(tank.getClass().getClassLoader(), tank.getClass().getInterfaces(), handler);
tankProxy.move();
}
}
输出结果:
start:1453883481492
坦克移动中...
end:1453883482493
total:1001
到此为止,利用jdk实现动态代理,原理是:将被代理对象Tank注入到InvocationHandler,通过自定义handler的invoke为Tank增加新特性,最后通过Proxy的newProxyInstance方法生成一个tankProxy对象(也就是tank的代理对象),通过tankProxy可以随意调用tank的方法(该方法必须是继承了共同的父类所具有的方法);也就是说jdk动态代理需要被代理对象tank和代理对象tankProxy实现共同的接口,这里是Moveable,所以tankProxy才可以调用move方法。
贴出类图:
图中Tank$Proxy并不是自己定义实现的,而是由Proxy类通过调用newProxyInstance方法生成的,生成的Tank$Proxy对象中维护一个自定义的InvocationHandler子类(TimeHandler),以达到动态代理的目的,下面我们模拟实现jdk中的InvocationHandler和Proxy类,更深入的理解动态代理
2、模拟jdk 动态代理的实现
先贴出Invocation接口,核心即invoke方法,通过invoke方法可以获得被代理对象o,和被代理方法m
public interface InvocationHandler {
public void invoke(Object o,Method m);
}
紧接着是Proxy的实现
public class Proxy {
public static Object newProxyInstance(Class interfaces, InvocationHandler h) throws Exception{
StringBuffer methodStr = new StringBuffer();
String tr = "\r\n";
//获取接口的所有方法
Method[] methods = interfaces.getMethods();
for(Method method:methods){
methodStr.append(
" public "+method.getReturnType()+" "+method.getName()+"(){"+tr+
" try{"+tr+
" java.lang.reflect.Method md = "+interfaces.getName() +"."+"class.getMethod(\""+method.getName()+"\");"+tr+
" m.invoke(this,md);"+tr+
" }catch(Exception e){ e.printStackTrace();}"+tr+
"}"+tr
);
}
//拼接代理类
String src = "package com.designPattern.dynamicProxy;"+tr+
"import com.designPattern.dynamicProxy.Moveable;"+tr+
"public class TimeProxy implements "+interfaces.getName()+" {"+tr+
" private com.designPattern.dynamicProxy.InvocationHandler m;"+tr+
" public TimeProxy(com.designPattern.dynamicProxy.InvocationHandler m){"+tr+
" this.m = m;"+tr+
" }"+tr+methodStr.toString()+
"}"+tr;
//创建代理类
String filename = System.getProperty("user.dir")+"/target/classes/com/designPattern/dynamicProxy/TimeProxy.java";
File file = new File(filename);
FileWriter writer = new FileWriter(file);
writer.write(src);
writer.flush();
writer.close();
//编译
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(filename);
CompilationTask cTask = compiler.getTask(null, fileMgr, null, null, null, units);
cTask.call();
fileMgr.close();
//加载到内存
Class c = ClassLoader.getSystemClassLoader().loadClass("com.designPattern.dynamicProxy.TimeProxy");
Constructor constructor = c.getConstructor(InvocationHandler.class);
Object m = constructor.newInstance(h);
return m;
}
}
Proxy的实现其实很简单:1)构造一个代理类的java文件(该代理类必然维护InvocationHandler实例,并且与代理类实现相同的接口,并实现所有的接口方法,每个方法的实现中都是调用自定义handler的invoke方法,handler为被代理对象提供额外的特性)。2)将该java文件编译为class文件。3)将编译好的class文件加载到内存。4)反射调用构造方法,并将InvocationHandler注入,得到代理对象
生成的java类代码如下:
package com.designPattern.dynamicProxy;
import com.designPattern.dynamicProxy.Moveable;
public class TimeProxy implements com.designPattern.dynamicProxy.Moveable {
private com.designPattern.dynamicProxy.InvocationHandler m;
public TimeProxy(com.designPattern.dynamicProxy.InvocationHandler m){
this.m = m;
}
public void move(){
try{
java.lang.reflect.Method md = com.designPattern.dynamicProxy.Moveable.class.getMethod("move");
m.invoke(this,md);
}catch(Exception e){ e.printStackTrace();}
}
}
这里换一种写法,采用匿名内部类方式构造InvocationHandler子类:
public class TankProxy {
@SuppressWarnings("unchecked")
public static <T> T getBean(final Object tank) throws Exception{
return (T)Proxy.newProxyInstance(tank.getClass().getInterfaces()[0], new InvocationHandler() {
@Override
public void invoke(Object o, Method m) {
long start = System.currentTimeMillis();
System.out.println("start:"+start);
try {
m.invoke(tank, new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("end:"+end);
System.out.println("time:"+(end-start));
}
});
}
}
测试:
public class ProxyTest {
public static void main(String[] args) throws Exception {
Tank tank = new Tank();
Moveable moveable = TankProxy.getBean(tank);
moveable.move();
}
}
输出结果:
start:1453887969913
坦克移动中...
end:1453887970913
time:1000
至此,模拟jdk实现动态代理完毕,为帮助理解,下面贴出类图: