设计模式学习笔记(三)动态代理模式
2016-02-16 15:34
519 查看
上节学习的聚合方式实现代理模式,属于静态代理,有个缺点,当我们要用到很多种代理的时候(A接口代理,B接口代理.....),代理类也会泛滥
jdk中的Proxy类实现了动态代理功能,现在来模拟一下。
生成步骤:
1.动态生成代理类的字符串代码,并写入到临时文件
2.JDK API动态编译
3.载入内存并实例化
也可以不用临时文件方式进行动态编译,还可直接生成二进制代码,CGLib,asm就是这么做的。
上面的动态代理很简陋,如果我想生成别的接口类的代理呢?
那就当接口名称作为参数传入嘛,但实现接口方法又比较麻烦了。
上面的代理模式也不太好,代理功能只是个计算运行时间而已,且代码被固定死了,如果我想做其他功能的代理,难道还要再写个方法newInstance2不成?
解决办法:
将代理功能的方法当作做成接口,代理实现者必须实现这个接口并传入到newInstance方法作为参数
取得代理实例时要传入InvocationHandler实例
jdk中的Proxy类实现了动态代理功能,现在来模拟一下。
生成步骤:
1.动态生成代理类的字符串代码,并写入到临时文件
2.JDK API动态编译
3.载入内存并实例化
package com.skymr.pattern.proxypat.dynamic; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLClassLoader; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import com.skymr.pattern.proxypat.IStudent; /** * 模拟jdk的Proxy类 * @author skymr * */ public class Proxy { /** * 通过jdk1.6的JavaCompiler API动态编译 * 还可以用其他方式动态编译,CGLib,ASM等第三方jar包 * @param student * @return * @throws Exception */ public static Object newInstance(IStudent student) throws Exception{ String javaSrc = ""; javaSrc += "package com.skymr.pattern.proxypat.dynamic;"; javaSrc += "public class StudentD implements com.skymr.pattern.proxypat.IStudent{"; javaSrc += "private com.skymr.pattern.proxypat.IStudent student;"; javaSrc += "public StudentD(com.skymr.pattern.proxypat.IStudent student){"; javaSrc += "this.student = student;"; javaSrc += "}"; javaSrc += "public void examine() {"; javaSrc += "long start = System.currentTimeMillis();"; javaSrc += "student.examine();"; javaSrc += "long end = System.currentTimeMillis();"; javaSrc += "System.out.println(\"耗时:\"+(end-start));"; javaSrc += "}"; javaSrc += "}"; String fileName = System.getProperty("user.home")+"/src/com/skymr/pattern/proxypat/dynamic/StudentD.java"; //格式化url fileName = DirUtil.formatURL(fileName); DirUtil.mkdir(DirUtil.getParentPath(fileName)); //写代理类代码写入到临时文件中 FileWriter writer = new FileWriter(new File(fileName)); writer.write(javaSrc); writer.flush(); writer.close(); //动态编译 //编译需要jdk1.6以上,更低的版本也可以,但没有公开api //jre运行环境不能使用单独的jre,必须使用jdk的jre //安装jdk时,有两个jre,一个是独立的,一个是jdk内的 JavaCompiler complier = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = complier.getStandardFileManager(null,null,null); CompilationTask task = complier.getTask(null, fileMgr, null, null, null, fileMgr.getJavaFileObjects(fileName)); task.call(); fileMgr.close(); //载入内存 //使用URLClassLoader,不能使用ClassLoader, //因为ClassLoader是加载classpath和bin目录下的class文件 URLClassLoader classloadedr = new URLClassLoader(new URL[]{new URL("file:/"+System.getProperty("user.home")+"/src/")}); Class c = classloadedr.loadClass("com.skymr.pattern.proxypat.dynamic.StudentD"); //实例化 //因为c.newInstance调用的是无参构造器,而代理类的构造器是有参的, //所以不能使用c.newInstance实例化 Constructor constuctor = c.getConstructor(IStudent.class); return constuctor.newInstance(student); } }
public static void main(String[] args) throws Exception { IStudent stuA = new StudentA(); IStudent stuD = (IStudent)Proxy.newInstance(stuA); stuD.examine(); }
也可以不用临时文件方式进行动态编译,还可直接生成二进制代码,CGLib,asm就是这么做的。
上面的动态代理很简陋,如果我想生成别的接口类的代理呢?
那就当接口名称作为参数传入嘛,但实现接口方法又比较麻烦了。
public static Object newInstance(Object obj, Class interClass) throws Exception{ String javaSrc = ""; String clazz = "ProxyClazz"; javaSrc += "package com.skymr.pattern.proxypat.dynamic;"; javaSrc += "public class "+clazz+" implements "+interClass.getName()+"{\r\n"; javaSrc += "private "+interClass.getName()+" student;"; javaSrc += "public "+clazz+"("+interClass.getName()+" student){\r\n"; javaSrc += "this.student = student;"; javaSrc += "}\r\n"; //实现所有接口方法 String methodStr = ""; Method[] methods = interClass.getMethods(); //简单实现接口无参无返回值方法,细节就不管了 for(Method method : methods){ methodStr += "public void "+method.getName()+"(){"; methodStr += "long start = System.currentTimeMillis();"; methodStr += "student."+method.getName()+"();"; methodStr += "long end = System.currentTimeMillis();"; methodStr += "System.out.println(\"耗时:\"+(end-start));"; methodStr += "}\r\n"; } javaSrc += methodStr; javaSrc += "}"; String fileName = System.getProperty("user.home")+"/src/com/skymr/pattern/proxypat/dynamic/"+clazz+".java"; //格式化url fileName = DirUtil.formatURL(fileName); DirUtil.mkdir(DirUtil.getParentPath(fileName)); //写代理类代码写入到临时文件中 FileWriter writer = new FileWriter(new File(fileName)); writer.write(javaSrc); writer.flush(); writer.close(); //动态编译 //编译需要jdk1.6以上,更低的版本也可以,但没有公开api //jre运行环境不能使用单独的jre,必须使用jdk的jre //安装jdk时,有两个jre,一个是独立的,一个是jdk内的 JavaCompiler complier = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = complier.getStandardFileManager(null,null,null); CompilationTask task = complier.getTask(null, fileMgr, null, null, null, fileMgr.getJavaFileObjects(fileName)); task.call(); fileMgr.close(); //载入内存 //使用URLClassLoader,不能使用ClassLoader, //因为ClassLoader是加载classpath和bin目录下的class文件 URLClassLoader classloadedr = new URLClassLoader(new URL[]{new URL("file:/"+System.getProperty("user.home")+"/src/")}); Class c = classloadedr.loadClass("com.skymr.pattern.proxypat.dynamic."+clazz); //实例化 //因为c.newInstance调用的是无参构造器,而代理类的构造器是有参的, //所以不能使用c.newInstance实例化 Constructor constuctor = c.getConstructor(interClass); return constuctor.newInstance(obj); }
上面的代理模式也不太好,代理功能只是个计算运行时间而已,且代码被固定死了,如果我想做其他功能的代理,难道还要再写个方法newInstance2不成?
解决办法:
将代理功能的方法当作做成接口,代理实现者必须实现这个接口并传入到newInstance方法作为参数
package com.skymr.pattern.proxypat.dynamic; import java.lang.reflect.Method; /** * 调用处理器,代理使用者必须实现 * @author skymr * */ public interface InvocationHandler { /** * 决定怎样调用被代理类的方法 * 被代理类方法前后需要做什么样的事情 * @param instance 被代理类实例 * @param method 被代理类方法 * @throws Exception */ public void invoke(Object instance, Method method)throws Exception; }
package com.skymr.pattern.proxypat.dynamic; import java.lang.reflect.Method; /** * 时间处理器,计算时间 * @author skymr * */ public class TimeHandler implements InvocationHandler { @Override public void invoke(Object instance, Method method) throws Exception{ long start = System.currentTimeMillis(); method.invoke(instance, null); long end = System.currentTimeMillis(); System.out.println("耗时:"+(end-start)); } }
@SuppressWarnings({ "unchecked", "rawtypes" }) public static Object newInstance(Object obj, Class interClass, InvocationHandler handler) throws Exception{ String javaSrc = ""; String clazz = "ProxyClazz"; javaSrc += "package com.skymr.pattern.proxypat.dynamic;"; javaSrc += "public class "+clazz+" implements "+interClass.getName()+"{\r\n"; javaSrc += "private "+interClass.getName()+" student;"; javaSrc += "private com.skymr.pattern.proxypat.dynamic.InvocationHandler handler;"; javaSrc += "public "+clazz+"("+interClass.getName()+" student,com.skymr.pattern.proxypat.dynamic.InvocationHandler handler){\r\n"; javaSrc += "this.student = student;"; javaSrc += "this.handler = handler;"; javaSrc += "}\r\n"; //实现所有接口方法 String methodStr = ""; Method[] methods = interClass.getMethods(); //简单实现接口无参无返回值方法,细节就不管了 for(Method method : methods){ methodStr += "public void "+method.getName()+"(){"; //必须捕获异常 methodStr += "try{"; methodStr += "java.lang.reflect.Method method = student.getClass().getMethod(\""+method.getName()+"\");\r\n"; methodStr += "handler.invoke(student,method);}"; methodStr += "catch(Exception e){}"; methodStr += "}\r\n"; } javaSrc += methodStr; javaSrc += "}"; String fileName = System.getProperty("user.home")+"/src/com/skymr/pattern/proxypat/dynamic/"+clazz+".java"; //格式化url fileName = DirUtil.formatURL(fileName); DirUtil.mkdir(DirUtil.getParentPath(fileName)); //写代理类代码写入到临时文件中 FileWriter writer = new FileWriter(new File(fileName)); writer.write(javaSrc); writer.flush(); writer.close(); //动态编译 //编译需要jdk1.6以上,更低的版本也可以,但没有公开api //jre运行环境不能使用单独的jre,必须使用jdk的jre //安装jdk时,有两个jre,一个是独立的,一个是jdk内的 JavaCompiler complier = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = complier.getStandardFileManager(null,null,null); CompilationTask task = complier.getTask(null, fileMgr, null, null, null, fileMgr.getJavaFileObjects(fileName)); task.call(); fileMgr.close(); //载入内存 //使用URLClassLoader,不能使用ClassLoader, //因为ClassLoader是加载classpath和bin目录下的class文件 URLClassLoader classloadedr = new URLClassLoader(new URL[]{new URL("file:/"+System.getProperty("user.home")+"/src/")}); Class c = classloadedr.loadClass("com.skymr.pattern.proxypat.dynamic."+clazz); //实例化 //因为c.newInstance调用的是无参构造器,而代理类的构造器是有参的, //所以不能使用c.newInstance实例化 Constructor constuctor = c.getConstructor(interClass,InvocationHandler.class); return constuctor.newInstance(obj,handler); }
取得代理实例时要传入InvocationHandler实例
public static void main(String[] args) throws Exception { IStudent stuA = new StudentA(); IStudent stuD = (IStudent)Proxy2.newInstance(stuA,IStudent.class,new TimeHandler()); stuD.examine(); }
相关文章推荐
- 1004. Counting Leaves (30)
- 09、Action_4(反动作、速度动作、静止动作、回调动作、拷贝动作)
- openstack学习指南
- NSNumber转变成 NSString 类型
- apache2.2后修改最大并发连接数
- 白话经典算法系列之六 快速排序 快速搞定
- hadoop学习点滴,积累
- 你应该知道的RPC原理
- Linux常用命令之三
- Edge Extraction -- 边缘提取
- haproxy配置文件
- Mysql where in中的参数传入字符串(字符串拼接)
- 将TextView设置根据dimens.xml中值设置字体大小
- NameNode 和 Secondary NameNode 的区别和作用
- Java正则表达式详解
- 关于this
- Qt应用程序打包
- java常用算法之最长回文子串(Longest Palindromic Substring)
- 使用Badboy自动录制脚本
- if语句 return;结尾