终于把动态代理的视频看完了。那视频长的可谓“浩浩汤汤,横无际涯”。不过马士兵老师将的还不错。很多细节问题可以先不去深究,先来看看脉络。
所谓动态代理,即DynamicProxy。现在有一个接口Moveable,里面有个move方法,任何可移动的物体都可以继承它。
- public interface Moveable {
- void move();
- }
Tank类实现了moveable接口,并且有它自己的move逻辑。
- public class Tank implements Moveable {
- @Override
- public void move() {
- System.out.println("Tank Moving...");
- try {
- Thread.sleep(new Random().nextInt(10000));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
现在我想在这个方法上判断一下这个方法运行了多长时间,或者做个日志之类的。最简单的方法就是直接在方法体上加代码,但是对于某些源码不可见,就不能加上自己的代码了。这时候可以用继承或者聚合。继承就是写一个TimeProxy继承Tank,在Tank的move方法前后加上些逻辑;聚合就是在TimeProxy中添加成员变量Tank。显然聚合更好,因为:想再move方法上想记录日志,判断权限,事务控制等等功能上的叠加,如先记录日志再记录时间。用继承的话要再加一个日志,要是先记录时间再记录日志呢?那又得再写一个类。而用聚合的话,各个Proxy都实现moveable,里面有个Tank成员,实现功能上的叠加就简单多了。。
- Tank t = new Tank();
- TankTimeProxy ttp = new TankTimeProxy(t);
- TankLogProxy tlp = new TankLogProxy(ttp);
这时候简单的静态代理就实现了。
如果相对任意对象的任意方法调用任何功能。前提:被代理的对象都实现了某个接口A。
首先有个一Proxy类,里面有一个newProxyInstance方法,它传入参数为被代理对象实现的接口A,处理逻辑(如添加日志)对象H。在newProxyInstance方法里面,它获取接口A的所有方法(反射),动态的生成一段代码(日志代理类,这个过程最难,但细节问题不去深究~),实现接口A,并且在方法体内,调用H的invoke方法(见下面代码),传入要做代理的那个方法。里面先实现日志,再进行方法调用(反射)。这样,以后任意多个实现同一接口的对象想做日志、时间、事物,Proxy和InvocationHandler都不必再更改,只需自己再写一个实现类实现InvocationHandler,里面写上自己的逻辑。
- public interface InvocationHandler {
- public void invoke(Object o, Method m);
- }
- public class TimeHandler implements InvocationHandler{
- private Object target; //被代理对象
- public TimeHandler(Object target) {
- super();
- this.target = target;
- }
- @Override
- public void invoke(Object o, Method m) { //代理类对象,被代理对象调用的方法
- long start = System.currentTimeMillis();
- System.out.println("starttime:" + start);
- System.out.println(o.getClass().getName());
- try {
- m.invoke(target);
- } catch (Exception e) {
- e.printStackTrace();
- }
- long end = System.currentTimeMillis();
- System.out.println("time:" + (end-start));
- }
- }
最后调用:
- Tank t = new Tank();
- InvocationHandler h = new TimeHandler(t);
- Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);//m就是一个代理
- m.move();
附:Proxy的具体实现(不重要)
- package com.bjsxt.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.StandardJavaFileManager;
- import javax.tools.ToolProvider;
- import javax.tools.JavaCompiler.CompilationTask;
- public class Proxy {
- public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM
- String methodStr = "";
- String rt = "\r\n";
- Method[] methods = infce.getMethods(); for(Method m : methods) {
- methodStr += "@Override" + rt +
- "public void " + m.getName() + "() {" + rt +
- " try {" + rt +
- " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
- " h.invoke(this, md);" + rt +
- " }catch(Exception e) {e.printStackTrace();}" + rt +
- " }";
- }
- String src =
- "package com.bjsxt.proxy;" + rt +
- "import java.lang.reflect.Method;" + rt +
- "public class $Proxy1 implements " + infce.getName() + "{" + rt +
- " public $Proxy1(InvocationHandler h) {" + rt +
- " this.h = h;" + rt +
- " }" + rt +
- " com.bjsxt.proxy.InvocationHandler h;" + rt +
- methodStr + rt +
- "}";
- String fileName =
- "d:/src/com/bjsxt/proxy/$Proxy1.java";
- File f = new File(fileName);
- FileWriter fw = new FileWriter(f);
- fw.write(src);
- fw.flush();
- fw.close();
- //compile
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); //JDK1.6 编译API
- StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
- Iterable units = fileMgr.getJavaFileObjects(fileName);
- CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
- t.call();
- fileMgr.close();
- //load into memory and create an instance
- URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
- URLClassLoader ul = new URLClassLoader(urls);
- Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
- System.out.println(c);
- Constructor ctr = c.getConstructor(InvocationHandler.class);
- Object m = ctr.newInstance(h);
- //m.move();
- return m;
- }
- }
以这种方式再原来的业务基础上加逻辑,可扩展性好,可以很方便添加和撤销。像struts2里面的拦截器,Spring中的AOP,都是动态代理的一种应用。
当然,动态代理在JDK中也有自己的实现。在java.lang.reflect包中可以找到。
源地址:http://yzmduncan.iteye.com/blog/787548