JAVA 设计模式之——动态代理

      终于把动态代理的视频看完了。那视频长的可谓“浩浩汤汤,横无际涯”。不过马士兵老师将的还不错。很多细节问题可以先不去深究,先来看看脉络。

      所谓动态代理,即DynamicProxy。现在有一个接口Moveable,里面有个move方法,任何可移动的物体都可以继承它。

Java代码   收藏代码
  1. public interface Moveable {  
  2.     void move();  
  3.       
  4. }  

 

Tank类实现了moveable接口,并且有它自己的move逻辑。

Java代码   收藏代码
  1. public class Tank implements Moveable {  
  2.   
  3.     @Override  
  4.     public void move() {  
  5.           
  6.         System.out.println("Tank Moving...");  
  7.         try {  
  8.             Thread.sleep(new Random().nextInt(10000));  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.           
  13.     }  
  14. }  

 现在我想在这个方法上判断一下这个方法运行了多长时间,或者做个日志之类的。最简单的方法就是直接在方法体上加代码,但是对于某些源码不可见,就不能加上自己的代码了。这时候可以用继承或者聚合。继承就是写一个TimeProxy继承Tank,在Tank的move方法前后加上些逻辑;聚合就是在TimeProxy中添加成员变量Tank。显然聚合更好,因为:想再move方法上想记录日志,判断权限,事务控制等等功能上的叠加,如先记录日志再记录时间。用继承的话要再加一个日志,要是先记录时间再记录日志呢?那又得再写一个类。而用聚合的话,各个Proxy都实现moveable,里面有个Tank成员,实现功能上的叠加就简单多了。。

Java代码   收藏代码
  1. Tank t = new Tank();  
  2. TankTimeProxy ttp = new TankTimeProxy(t);  
  3. TankLogProxy tlp = new TankLogProxy(ttp);  

 这时候简单的静态代理就实现了。

 

 如果相对任意对象的任意方法调用任何功能。前提:被代理的对象都实现了某个接口A。

首先有个一Proxy类,里面有一个newProxyInstance方法,它传入参数为被代理对象实现的接口A,处理逻辑(如添加日志)对象H。在newProxyInstance方法里面,它获取接口A的所有方法(反射),动态的生成一段代码(日志代理类,这个过程最难,但细节问题不去深究~),实现接口A,并且在方法体内,调用H的invoke方法(见下面代码),传入要做代理的那个方法。里面先实现日志,再进行方法调用(反射)。这样,以后任意多个实现同一接口的对象想做日志、时间、事物,Proxy和InvocationHandler都不必再更改,只需自己再写一个实现类实现InvocationHandler,里面写上自己的逻辑。

Java代码   收藏代码
  1. public interface InvocationHandler {  
  2.     public void invoke(Object o, Method m);  
  3. }  
Java代码   收藏代码
  1. public class TimeHandler implements InvocationHandler{  
  2.     private Object target;  //被代理对象  
  3.   
  4.     public TimeHandler(Object target) {  
  5.         super();  
  6.         this.target = target;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void invoke(Object o, Method m) {    //代理类对象,被代理对象调用的方法  
  11.         long start = System.currentTimeMillis();  
  12.         System.out.println("starttime:" + start);  
  13.         System.out.println(o.getClass().getName());  
  14.         try {  
  15.             m.invoke(target);  
  16.         } catch (Exception e) {  
  17.             e.printStackTrace();  
  18.         }  
  19.         long end = System.currentTimeMillis();  
  20.         System.out.println("time:" + (end-start));  
  21.     }  
  22.   
  23. }  

  最后调用:

Java代码   收藏代码
  1. Tank t = new Tank();  
  2. InvocationHandler h = new TimeHandler(t);  
  3. Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);//m就是一个代理  
  4. m.move();  

附:Proxy的具体实现(不重要)

Java代码   收藏代码
  1. package com.bjsxt.proxy;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileWriter;  
  5. import java.lang.reflect.Constructor;  
  6. import java.lang.reflect.Method;  
  7. import java.net.URL;  
  8. import java.net.URLClassLoader;  
  9.   
  10. import javax.tools.JavaCompiler;  
  11. import javax.tools.StandardJavaFileManager;  
  12. import javax.tools.ToolProvider;  
  13. import javax.tools.JavaCompiler.CompilationTask;  
  14.   
  15. public class Proxy {  
  16.     public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM  
  17.         String methodStr = "";  
  18.         String rt = "\r\n";  
  19.           
  20.         Method[] methods = infce.getMethods();      for(Method m : methods) {  
  21.             methodStr += "@Override" + rt +   
  22.                          "public void " + m.getName() + "() {" + rt +  
  23.                          "    try {" + rt +  
  24.                          "        Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +  
  25.                          "        h.invoke(this, md);" + rt +  
  26.                          "    }catch(Exception e) {e.printStackTrace();}" + rt +  
  27.                           
  28.                          "  }";  
  29.         }  
  30.           
  31.         String src =   
  32.             "package com.bjsxt.proxy;" +  rt +  
  33.             "import java.lang.reflect.Method;" + rt +  
  34.             "public class $Proxy1 implements " + infce.getName() + "{" + rt +  
  35.             "    public $Proxy1(InvocationHandler h) {" + rt +  
  36.             "        this.h = h;" + rt +  
  37.             "    }" + rt +  
  38.               
  39.               
  40.             "    com.bjsxt.proxy.InvocationHandler h;" + rt +  
  41.                               
  42.             methodStr + rt +  
  43.             "}";  
  44.         String fileName =   
  45.             "d:/src/com/bjsxt/proxy/$Proxy1.java";  
  46.         File f = new File(fileName);  
  47.         FileWriter fw = new FileWriter(f);  
  48.         fw.write(src);  
  49.         fw.flush();  
  50.         fw.close();  
  51.           
  52.         //compile  
  53.         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();       //JDK1.6 编译API  
  54.         StandardJavaFileManager fileMgr = compiler.getStandardFileManager(nullnullnull);  
  55.         Iterable units = fileMgr.getJavaFileObjects(fileName);  
  56.         CompilationTask t = compiler.getTask(null, fileMgr, nullnullnull, units);  
  57.         t.call();  
  58.         fileMgr.close();  
  59.           
  60.         //load into memory and create an instance  
  61.         URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};  
  62.         URLClassLoader ul = new URLClassLoader(urls);  
  63.         Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");  
  64.         System.out.println(c);  
  65.           
  66.         Constructor ctr = c.getConstructor(InvocationHandler.class);  
  67.         Object m = ctr.newInstance(h);  
  68.         //m.move();  
  69.   
  70.         return m;  
  71.     }  
  72. }  

  

 

以这种方式再原来的业务基础上加逻辑,可扩展性好,可以很方便添加和撤销。像struts2里面的拦截器,Spring中的AOP,都是动态代理的一种应用。

 

当然,动态代理在JDK中也有自己的实现。在java.lang.reflect包中可以找到。

 

源地址:http://yzmduncan.iteye.com/blog/787548

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值