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实现动态代理完毕,为帮助理解,下面贴出类图: