设计模式之Proxy(代理):模拟JDK的动态代理
2016-01-27 11:11
721 查看
1.静态代理
代理的实现方式有两种,一是继承,二是聚合。示例如下:计算Tank中move方法的运行时间,不包括JDK为其准备运行环境的时间。
2.动态代理
如果有很多对象,要想对任意对象、任意的接口方法,实现任意代理,又改怎么办呢?答案是动态代理,下面我们简单模拟JDK的动态代理。
运行结果如下:
其中Proxy类动态生成的代理类为MyProxy.java,如下:
代理的实现方式有两种,一是继承,二是聚合。示例如下:计算Tank中move方法的运行时间,不包括JDK为其准备运行环境的时间。
package com.zj.proxy; public interface Moveable { void move(); void stop(); }
package com.zj.proxy; import java.util.Random; 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(); } } @Override public void stop() { System.out.println("Tank stop...."); } }
package com.zj.proxy; public class TankExtendProxy extends Tank{ /* * 方法1:继承Tank,复写move方法,前后加上时间计时,然后相减。 * TankExtendProxy相当于Tank的代理:调用TankExtendProxy的move时, * 总会调用TankExtendProxy父类的move,即相当于调用Tank的move。 */ @Override public void move() { long start=System.currentTimeMillis(); super.move(); long end=System.currentTimeMillis(); //计算方法的运行时间 System.out.println("time="+(end-start)); } }
package com.zj.proxy; public class TankTimeProxy implements Moveable{ Moveable t; public TankTimeProxy(Moveable t) { super(); this.t = t; } /* * 方法2:聚合(一个类中有另外一个类的对象)。Tank类型的成员变量,实现Moveable接口,复写move方法,调用Tank对象的move方法,前后加上时间计时,然后相减。 * TankTimeProxy相当于Tank的代理:调用TankTimeProxy的move即相当于调用Tank的move。 */ @Override public void move() { long start=System.currentTimeMillis(); System.out.println("Time proxy: Start time...."); t.move(); long end=System.currentTimeMillis(); //计算方法的运行时间 System.out.println("Time proxy: run time= "+(end-start)); } @Override public void stop() { } }
package com.zj.proxy; public class TankLogProxy implements Moveable{ Moveable t; public TankLogProxy(Moveable t) { super(); this.t = t; } @Override public void move() { System.out.println("Log Proxy: Tank start move...."); t.move(); System.out.println("Log Proxy: Tank stop...."); } @Override public void stop() { } }
package com.zj.proxy; import org.junit.Test; /* * 1.代理的实现:计算move的运行时间,不包括JDK为其准备运行环境的时间 * 方法1:继承Tank,复写move方法,前后加上时间计时,然后相减。 * 方法2:聚合(一个类中有另外一个类的对象)。Tank类型的成员变量,实现Moveable接口,复写move方法,调用Tank对象的move方法,前后加上时间计时,然后相减。 * * 2.上述两种方法都是对Tank的代理,问哪种方法好? * 答:聚合比继承好。 */ public class Client { public static void main(String[] args) { Moveable m=new Tank(); m.move(); } //测试继承方式实现的代理 @Test public void testTankExtendProxy() { TankExtendProxy tep=new TankExtendProxy(); tep.move(); } //测试聚合方式实现代理:先记录时间,后记录日志 @Test public void testTimeLog(){ Tank t=new Tank(); TankTimeProxy ttp=new TankTimeProxy(t); TankLogProxy tlp=new TankLogProxy(ttp); Moveable m=tlp; m.move(); } //测试聚合方式实现代理:先记录日志,后记录时间 @Test public void testLogTime(){ Tank t=new Tank(); TankLogProxy tlp=new TankLogProxy(t); TankTimeProxy ttp=new TankTimeProxy(tlp); Moveable m=ttp; m.move(); } }
2.动态代理
如果有很多对象,要想对任意对象、任意的接口方法,实现任意代理,又改怎么办呢?答案是动态代理,下面我们简单模拟JDK的动态代理。
package com.zj.proxy2; public interface Moveable { void move(); }
package com.zj.proxy2; import java.util.Random; 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(); } } }
package com.zj.proxy2; 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 interf, InvocationHandler h) throws Exception{ /**********************source code*******************************/ String rt="\r\n"; String methodStr=""; Method[] methods= interf.getMethods(); for(Method m:methods){ methodStr += " @Override" + rt + " public " + m.getReturnType()+" "+m.getName()+"(){" + rt + " try {" + rt + " Method md="+interf.getName()+".class.getMethod(\""+m.getName()+"\");"+ rt + " h.invoke(this,md);" + rt + " } catch(Exception e){e.printStackTrace();}" + rt + " }"; } String src= "package com.zj.proxy2;" + rt + "import java.lang.reflect.Method;" + rt + "public class MyProxy implements "+interf.getName()+" {" + rt + " InvocationHandler h;" + rt + " public MyProxy(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + methodStr + rt + "}"; //拿到系统的当前目录 String fileName=System.getProperty("user.dir")+"\\src\\com\\zj\\proxy2\\MyProxy.java"; //System.out.println(fileName);//输出结果为F:\sys_zj\Proxy\src\com\zj\proxy2\MyProxy.java File f=new File(fileName); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); /**********************compile*******************************/ //Java的编译器,调用ToolProvider.getSystemJavaCompiler()可以拿到系统当前默认的Java编译器,即Javac JavaCompiler compiler =ToolProvider.getSystemJavaCompiler(); //System.out.println(compiler.getClass().getName());//输出结果为com.sun.tools.javac.api.JavacTool 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*******************************/ //把硬盘上的java文件放到内存中 URL[] urls=new URL[]{new URL("file:\\"+ System.getProperty("user.dir")+"\\src")}; URLClassLoader ul=new URLClassLoader(urls); Class c=ul.loadClass("com.zj.proxy2.MyProxy"); //System.out.println(c);//输出结果为class com.zj.proxy2.MyProxy /**********************create an instance*******************************/ //拿到构造方法 Constructor ctr=c.getConstructor(InvocationHandler.class); //获得代理对象 Object obj=ctr.newInstance(h); return obj; } }
package com.zj.proxy2; import java.lang.reflect.Method; public interface InvocationHandler { public void invoke(Object obj,Method m); }
package com.zj.proxy2; import java.lang.reflect.Method; public class TimeHandler implements InvocationHandler { //被代理对象 private Object target; public TimeHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object obj,Method m) { long start=System.currentTimeMillis(); System.out.println("Time proxy: Start time...."); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } long end=System.currentTimeMillis(); System.out.println("Time proxy: run time= "+(end-start)); } }
package com.zj.proxy2; import java.lang.reflect.Method; public class LogHandler implements InvocationHandler{ //被代理对象 private Object target; public LogHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object obj,Method m) { System.out.println("Log Proxy: Tank start move...."); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } long end=System.currentTimeMillis(); System.out.println("Log Proxy: Tank stop...."); } }
package com.zj.proxy2; /* * 动态代理的好处:可以对任意的对象、任意的接口方法,实现任意的代理;还可以实现代理的叠加 * * 1.什么是动态代理? * 2.动态代理是怎么产生的? * 3.动态代理的作用? */ public class Client { public static void main(String[] args) throws Exception { Tank t=new Tank(); InvocationHandler h1=new TimeHandler(t); Moveable m1=(Moveable)Proxy.newProxyInstance(Moveable.class, h1); InvocationHandler h2=new LogHandler(m1); Moveable m2=(Moveable)Proxy.newProxyInstance(Moveable.class, h2); m2.move(); } }
运行结果如下:
其中Proxy类动态生成的代理类为MyProxy.java,如下:
package com.zj.proxy2; import java.lang.reflect.Method; public class MyProxy implements com.zj.proxy2.Moveable { InvocationHandler h; public MyProxy(InvocationHandler h) { this.h = h; } @Override public void move(){ try { Method md=com.zj.proxy2.Moveable.class.getMethod("move"); h.invoke(this,md); } catch(Exception e){e.printStackTrace();} } }
相关文章推荐
- JedisConnectionException: java.net.SocketException: Socket closed;Unknown reply: ; It seems like ser
- SpringJUnit4ClassRunner拉起来的单元测试怎么装配Container实例
- JAVA之关于this和super的用法
- Spring动态数据源的配置及使用
- java中的封装类
- SpringMVC之旅-开发到部署过程中遇到的问题整理(不断更新)
- Pascal's Triangle (leetcode java)
- java 注解 详解
- java 里特殊的String
- java使用正则表达式——实例
- 《JAVA与模式》之抽象工厂模式
- java正则表达式基本符号
- Java for Web学习笔记(十五):JSP(5)在JSP中使用Java吗?
- Java中过滤出字母、数字和中文的正则表达式
- [转] java中int,char,string三种类型的相互转换
- javaweb局部刷新-ajax异步请求springMVC显示返回的jsp内容,代替iframe
- spring property标签中的 ref属性和ref 标签有什么不同? 如下:<property name="a" ref="b" />
- Spring整合Quartz任务调度
- 招人:java和c,还有测试
- 在eclipse中查看sources源码和JavaDoc帮助文档