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

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值