- 浏览: 81836 次
- 性别:
- 来自: 广州
文章分类
最新评论
@2014年2月12日14:14:38
Java动态代理模式
1. 代理:一个角色代表别一个角色来完成某些特定的功能。
比如:生产商,中间商,客户这三者这间的关系
客户买产品并不直接与生产商打交道,也不用知道产品是如何产生的,客户只与中间商打交道,而中间商就可以对产品进行一些包装,提供一些售后的服务。
代理模式有三个角色: 1. 抽象主题角色 2. 代理主题角色 3. 实际被代理角色
其它类通过访问代理主题角色来访问实际被代理角色。
2. 下面我们来个一个静态代理的实现。
我以一个坦克为例。
抽象主题角色:Moveable
package com.bjsxt.proxy; public interface Moveable { void move(); }
实际被代理对象:Tank
package com.bjsxt.proxy; public class Tank implements Moveable{ @Override public void move() { System.out.println("TanK moving........"); } }
代理主题角色:TanktimeProxy
package com.bjsxt.proxy; public class TanktimeProxy implements Moveable{ private Moveable t; public TanktimeProxy(Moveable t) { super(); this.t = t; } @Override public void move() { long time1 = System.currentTimeMillis(); System.out.println("time1="+time1); t.move(); long time2 = System.currentTimeMillis(); System.out.println("time2="+time2); System.out.println("运行时间为:"+(time2-time1)); } }
测试:
package com.bjsxt.proxy; public class TestTank { public static void main(String[] args) { Tank t = new Tank(); Moveable move = new TanktimeProxy(t); move.move(); } }
从上例可以看到代理主题角色:TanktimeProxy实现了对Tank的move()方法运行时间的计算,而TanktimeProxy,Tank都实现了Moveable接口,通过调用TanktimeProxy的move()方法我们可以实现对Tank的move()方法的运行时间的计算,而不用在Tank的move()方法中作任何实现,这就是代理的作用。代理实现时TanktimeProxy,Tank必需实现Moveable接口。
下面我想在TanK的move()方法前后加上日志:
我必需再写一个类来实现这一功能:
package com.bjsxt.proxy; public class TanklogProxy implements Moveable{ private Moveable t; public TanklogProxy(Moveable t) { super(); this.t = t; } @Override public void move() { System.out.println("start move........"); t.move(); System.out.println("end move......"); } }测试:
package com.bjsxt.proxy; public class TestTank { public static void main(String[] args) { Tank t = new Tank(); Moveable move = new TanktimeProxy(t); Moveable movet = new TanklogProxy(move); movet.move(); } }这样我通过代理在Tank的move()方法前后加入了日志和时间统计的功能,由于TanktimeProxy,TanklogProxy都实现了Moveable接口,所以TanklogProxy可以代理TanktimeProxy,反过来也可以,它们对Tank的代理顺序是可以交换的。
如果我想在Tank的move()方法调用的前后加入更多的功能,是不是要写更多的代理主题角色,这样子会使得代码过于臃肿,不易于维护,那有没有什么办法可以解决呢,答案是可以的,我们可以动态的来生成代理主题角色,来代理所有的被代理对象,这就是动态代理。
下面是一个简单的动态代理的实现:
类图如下:
首先编写一个生成代理主题角色的类:Proxy
package com.bjsxt.DynamicProxy; 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 newProxyIntenct(Class infac,InvocationHandler h) throws Exception{ String br ="\r\n"; String methString =""; Method[] method = infac.getMethods(); for(Method m: method){ methString = " @Override"+ br + " public void "+m.getName()+"() {"+ br + " try {" + br + " Method md ="+ infac.getName()+".class.getMethod(\""+m.getName()+"\");"+ br + " h.invoke(this,md);" + br + " }catch (Exception e){ "+ br+ " e.printStackTrace();" + br + " }" + br + " }"; } String src = "package com.gjy.DynamicProxy;" + br + "import java.lang.reflect.Method;" + br + "public class $Proxy implements "+infac.getName()+"{" + br + " private com.gjy.DynamicProxy.InvocationHandler h;" + br + " public $Proxy(InvocationHandler h) {" + br + " super();" + br + " this.h = h;" + br + " }" + br + br + methString +br + "}"; MakFileUtil.createFile("D:/src/com/gjy/DynamicProxy"); //生成java文件 String fileName ="D:\\src\\com\\gjy\\DynamicProxy\\$Proxy.java"; System.out.println(fileName); File file = new File(fileName); FileWriter fWriter = new FileWriter(file); fWriter.write(src); fWriter.flush(); fWriter.close(); //生成class文件,jdk6提供的工具类 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); //System.out.println(compiler.getClass().getName()); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); Iterable units = fileManager.getJavaFileObjects(fileName); CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units); task.call(); fileManager.close(); //装载到内存,生成新对象 URL[] urls = new URL[]{new URL("file:/"+"D:\\src\\")}; URLClassLoader loader = new URLClassLoader(urls); Class c = loader.loadClass("com.gjy.DynamicProxy.$Proxy"); //通过有参的构造器反射生成代理类的实例 Constructor ctr = c.getConstructor(InvocationHandler.class); Object obj = (Object) ctr.newInstance(h); return obj; } }
代理对象的操作接口:
package com.bjsxt.DynamicProxy; import java.lang.reflect.Method; public interface InvocationHandler { void invoke(Object o,Method m); }
通过实现代理对象的操作接口实现对被代理对象的方法调用前后的逻辑操作。
TimeInvocationHandler实现InvocationHandler接口:
package com.bjsxt.DynamicProxy; import java.lang.reflect.Method; public class TimeInvocationHandler implements InvocationHandler { private Object target; public TimeInvocationHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object o, Method m) { long time1 = System.currentTimeMillis(); System.out.println("time1="+time1); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } long time2 = System.currentTimeMillis(); System.out.println("time2="+time2); System.out.println("Tank 的启动时间:"+(time2-time1)); } }
实际被代理对象:Tank
package com.bjsxt.DynamicProxy; public class Tank implements Moveable{ @Override public void move() { int a = 5; int b = 6; int c = 0; int d = 0; for (int i = 0; i < 1000; i++) { d = i; } c = ((a+b)/2)*12; System.out.println("TanK moving..Tank 的速度是"+c); }
抽象代理主题:Moveable
package com.bjsxt.DynamicProxy; public interface Moveable { void move(); } }
测试:
package com.bjsxt.DynamicProxy; public class TestTank { public static void main(String[] args) throws Exception{ Tank t = new Tank(); Moveable moveable = (Moveable) Proxy.newProxyIntenct(Moveable.class,new TimeInvocationHandler(t)); moveable.move(); } }创建文件夹工具类:MakFileUtil
package com.bjsxt.DynamicProxy; import java.io.File; import java.io.IOException; import java.util.StringTokenizer; public class MakFileUtil { public static void createFile(String pathstr) throws IOException{ // File dirFile; // boolean bFile; // bFile = false; // // dirFile = new File("E:\\test"); // bFile = dirFile.exists(); // // if( bFile == true ){ // System.out.println("The folder exists."); // }else{ // System.out.println("The folder do not exist,now trying to create a one..."); // bFile = dirFile.mkdir(); // if( bFile == true ){ // System.out.println("Create successfully!"); // }else{ // System.out.println("Disable to make the folder,please check the disk is full or not."); // System.exit(1); // } //创建多级目录 String path = pathstr; //为指定字符串构造一个 string tokenizer。 "/"字符是分隔标记的分隔符。分隔符字符本身不作为标记。 StringTokenizer st = new StringTokenizer(path,"/"); String path1 = st.nextToken()+"/"; String path2 = path1; while(st.hasMoreTokens()) { path1 = st.nextToken()+"/"; path2 += path1; File inbox = new File(path2); if(!inbox.exists()) inbox.mkdir(); } } }以上就是动态代理的一个模拟实现,测试时我们不管Proxy和InvocationHandler是怎么实现的,我们只要实现InvocationHandler接口完成相应的逻辑,然后调用Proxy的newProxyIntenct(Class infac, InvocationHandler h) 传入相应的接口,和InvocationHandler的实现类就可以实现对被代理对象的代理。也就是说Proxy和InvocationHandler写好之后永远不变。
在运行过程中Proxy会动态生成代理主题角色,示例中生成的代理主题角色的代码如下:
import java.lang.reflect.Method; public class $Proxy implements com.gjy.DynamicProxy.Moveable{ private com.gjy.DynamicProxy.InvocationHandler h; public $Proxy(MakFileUtil h) { super(); this.h = h; } @Override public void move() { try { Method md =com.gjy.DynamicProxy.Moveable.class.getMethod("move"); h.invoke(this,md); }catch (Exception e){ e.printStackTrace(); } } }如果我们想在Tank的move()方法被调用的前后加入其它的逻辑处理,我们只需实现InvocationHandler接口,下面是给move()加日志:
package com.bjsxt.DynamicProxy; import java.lang.reflect.Method; public class LogInvocationHandler implements InvocationHandler { private Object target; public LogInvocationHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object o, Method m) { System.out.println("Tank start..........."); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } System.out.println("Tank stop.............."); } }测试:
package com.bjsxt.DynamicProxy; public class TestTank { public static void main(String[] args) throws Exception{ Tank t = new Tank(); Moveable moveable = (Moveable) Proxy.newProxyIntenct(Moveable.class,new TimeInvocationHandler(t)); Moveable moveable2 = (Moveable) Proxy.newProxyIntenct(Moveable.class, new LogInvocationHandler(moveable)); moveable2.move(); } }同样代理顺序可以交换,这就是动态代理的实现过程。
发表评论
-
记录一下我的p2p的UDP打洞过程【云服务器公网版本】
2014-07-19 21:14 2101服务端连接了成功处理了两个客户端的登录并且成功打洞后的log记 ... -
记录一下我的p2p的UDP打洞过程【localhost局域网版本】
2014-07-19 21:08 2002MainServer在两个客户端都启动成功之后并且打洞成功之后 ... -
有hadoop_zookeeper的pom.xml
2014-06-03 23:39 831FROM:http://blog.csdn.net/zph2 ... -
省市县三级联动数据库
2014-06-03 09:42 3164/* *@名称: myaddress.js ... -
Java中的注解机制--比较详细
2014-05-31 17:33 555http://blog.csdn.net/a243932069 ... -
记录一下这次mvn 淘宝的开源MVC框架 Webx的过程
2014-05-24 21:44 815[INFO] ------------------ ... -
恶心的Enum类型
2014-05-24 09:59 663public enum InvoiceType { ... -
分页标签<w:pager>
2014-05-09 17:05 9741.WEB-INF/tlds/pager.tld < ... -
自定义标签taglib
2014-05-09 17:00 710JSP 最佳实践: taglib简介 ... -
MyEclipse has detected that less than 5% of the 31MB of Eden Space (Heap memory)
2014-04-27 12:48 1037解决办法: D:\>cd D:\Users\Admin ... -
数据库映射只@OneToMany的两个区别
2014-03-31 21:07 774========================== (不加 ... -
接着报错,连接问题Cannot open a connection
2014-03-28 18:39 1364HTTP Status 500 - ------- ... -
让spring启动时应用不需要加载数据库【default-lazy-init="true"】
2014-03-28 14:33 5321spring启动时应用不需要加载数据库,需要时在加载,如何实现 ... -
异常的Tomcat输出信息-修正log4j配置文件之后仍然异常
2014-03-28 11:51 10372014-3-28 11:46:50 org.apache ... -
异常的Tomcat输出信息
2014-03-28 11:27 660正常情况下是没有换行的。现在异常的情况是有换行的。 20 ... -
正常的Tomcat输出信息
2014-03-28 11:26 9572014-3-28 11:23:07 org.apache ... -
烦人的后台首页index.jsp弄好了
2014-03-25 14:39 45671.<title>微信公众平台后台管理< ... -
MAVEN官网地址
2014-03-25 10:12 677http://mvnrepository.com/artifa ... -
正则表达式
2014-03-25 10:11 515[正则表达式]文本框输入内容控制 整数或者小数:^[0-9]+ ... -
Window 通过cmd查看端口占用、相应进程、杀死进程等的命令
2014-03-24 16:03 1027Window 通过cmd查看端口占用、相应进程、杀死进程等的命 ...
相关推荐
马士兵老师spring框架学习笔记
马士兵JAVA笔记(全).docx
马士兵oracle笔记,浅显易懂。
jvm java虚拟机 调优 马士兵 笔记 让你对java虚拟机调优有初步的认识
linux 马士兵笔记
马士兵老师mybatis相关学习笔记
马士兵Spring课堂笔记(超级详细版).pdf
马士兵老师讲的io流课程,非常通俗易懂,举例很形象。
马士兵Shopping项目源代码,马士兵Shopping项目源代码,马士兵Shopping项目源代码,马士兵Shopping项目源代码,
马士兵老师HashMap学习笔记
马士兵j2se源码和ppt马士兵j2se源码和ppt马士兵j2se源码和ppt马士兵j2se源码和ppt马士兵j2se源码和ppt
马士兵 struts2 操作手册
java 马士兵 网上商城 源代码 可以运行的购物系统
马士兵hibernate文档.doc
马士兵多线程训练营笔记
一.java内存结构 2 二 垃圾收集算法: 3 三 JVM参数 4 四 JVM的垃圾回收集器 7 五 常用参数设置 7
是自己根据马士兵的NewShopping模仿着一步步做的,对项目的界面进行了美化,更加漂亮美观,并对视频中项目一些细节不足的地方进行了一些修正,并添加了部分功能进行完善,但是对于视频中报表和文件上传的功能我并...
马士兵Springboot课堂笔记
马士兵多线程训练营上课笔记