动态代理的使用和实现机制
Posted 有梦想的咸鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理的使用和实现机制相关的知识,希望对你有一定的参考价值。
工作中很久没有接触动态代理,之前的学习也有些模糊,导致有些遗忘,这里记录下个人对动态代理的理解,如有读者发现问题多多指正吧。
就java而言对于动态代理的支持多是以接口实现,其实现主要是通过java.lang.reflect.Proxy类,java.lang.reflect.InvocationHandler接口。Proxy类主要用于获取动态代理对象,InvocationHandler接口用来约束调用者实现。
动态代理运行机制:
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法会返回一个代理对象类的实例。当程序执行时会通过反射机制动态的生成一个代理类,该类实现一个接口里的方法(也就是说代理类与被代理类有相同的接口),在该代理类里面有一个InvocationHandler类型的成员变量,也就是调用处理程序,通过调用处理程序来给被代理类增强功能。创建好代理类后就调用类加载器将该类加载到类存,然后再通过反射创建一个该代理类的实例对象。下面是具体实现:
1 1.代理接口:Moveable.java 2 3 package com.test; 4 5 public interface Moveable { 6 7 void move(); 8 9 } 10 11 12 2.被代理对象:Tank.java 13 package com.test; 14 15 import java.util.Random; 16 17 public class Tank implements Moveable { 18 19 public void move() { 20 System.out.println("Tank moving..."); 21 try { 22 Thread.sleep(new Random().nextInt(10000)); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 } 27 28 } 29 30 3.为被代理对象产生一个代理类对象,其中是想增加记录运行时间的功能 31 32 package com.test; 33 34 import java.io.File; 35 import java.io.FileWriter; 36 import java.lang.reflect.Constructor; 37 import java.lang.reflect.Method; 38 39 import javax.tools.JavaCompiler; 40 import javax.tools.StandardJavaFileManager; 41 import javax.tools.ToolProvider; 42 import javax.tools.JavaCompiler.CompilationTask; 43 44 public class Proxy { 45 public static Object newProxyInstance(Class interfaces,InvocationHandler h)throws Exception{ 46 StringBuffer methodStr = new StringBuffer(); 47 String tr = "\\r\\n"; 48 Method[] methods = interfaces.getMethods(); 49 //拼接代理类的方法 50 for (Method method : methods) { 51 methodStr.append( 52 " public "+ method.getReturnType()+ " " +method.getName()+"() {" + tr + 53 " try {" + tr + 54 " java.lang.reflect.Method md = " + interfaces.getName() + "." + "class.getMethod(\\"" + method.getName() + "\\");" + tr + 55 " h.invoke(this,md);" + tr + 56 " }catch(Exception e) {e.printStackTrace();}" + tr + 57 " }" + tr 58 ); 59 } 60 61 //拼接代理类 62 String src = "package com.test;" + tr + 63 "import com.test.Moveable;" + tr + 64 "public class TimeProxy implements " + interfaces.getName() + " {" + tr + 65 " private com.test.InvocationHandler h;" + tr + 66 " public TimeProxy(com.test.InvocationHandler h) {" + tr + 67 " this.h = h;" + tr + 68 " }" + tr + 69 methodStr.toString() + tr + 70 "}"; 71 //创建代理类 72 String fileName = System.getProperty("user.dir") + "/src/com/test/TimeProxy.java"; 73 File file = new File(fileName); 74 FileWriter writer = new FileWriter(file); 75 writer.write(src); 76 writer.flush(); 77 writer.close(); 78 //编译 79 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 80 StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); 81 Iterable units = fileMgr.getJavaFileObjects(fileName); 82 CompilationTask ct = compiler.getTask(null, fileMgr, null, null, null, units); 83 ct.call(); 84 fileMgr.close(); 85 //加载类到内存: 86 Class c = ClassLoader.getSystemClassLoader().loadClass("com.test.TimeProxy"); 87 Constructor constructor = c.getConstructor(InvocationHandler.class); //得到参数为InvocationHandler类型的构造方法 88 Object m = constructor.newInstance(h); //通过该构造方法得到实例 89 return m; 90 91 } 92 } 93 94 4.TankProxy.java 95 96 package com.test; 97 98 import java.lang.reflect.Method; 99 100 public class TankProxy { 101 public static <T> T getBean(final Object tank) throws Exception{ 102 return (T)Proxy.newProxyInstance(tank.getClass().getInterfaces()[0], new InvocationHandler(){ 103 public void invoke(Object proxy, Method method) { 104 long start = System.currentTimeMillis(); 105 System.out.println("start:"+start); 106 try { 107 method.invoke(tank, new Object[]{}); 108 } catch (Exception e) { 109 e.printStackTrace(); 110 } 111 long end = System.currentTimeMillis(); 112 System.out.println("end:"+end); 113 System.out.println("time:"+(end-start)); 114 } 115 116 }); 117 } 118 } 119 120 5.测试程序: 121 122 package com.test; 123 124 import java.util.List; 125 126 import com.extend.Tank2; 127 import com.extend.Tank3; 128 import com.juhe.LogProxy; 129 import com.juhe.TimeProxy; 130 131 public class Test { 132 public static void main(String[] args) throws Exception { 133 Tank tank = new Tank(); 134 Moveable m = TankProxy.getBean(tank); 135 m.move(); 136 137 } 138 139 } 140 141 142 143 执行该程序的结果为: 144 start:1369121253400 145 Tank moving... 146 end:1369121260078 147 time:6678 148 149 150 151 动态生成的代理类的内容如下: 152 153 package com.test; 154 import com.test.Moveable; 155 public class TimeProxy implements com.test.Moveable { 156 private com.test.InvocationHandler h; 157 public TimeProxy(com.test.InvocationHandler h) { 158 this.h = h; 159 } 160 public void move() { 161 try { 162 java.lang.reflect.Method md = com.test.Moveable.class.getMethod("move"); 163 h.invoke(this,md); 164 }catch(Exception e) {e.printStackTrace();} 165 } 166 167 }
小结:动态代理在运行期通过接口动态生成代理类,这为其带来了一定的灵活性,但这个灵活性却带来了两个问题,第一代理类必须实现一个接口,如果没实现接口会抛出一个异常。第二性能影响,因为动态代理使用反射的机制实现的,首先反射肯定比直接调用要慢,其次使用反射大量生成类文件可能引起Full GC造成性能影响,因为字节码文件加载后会存放在JVM运行时区的方法区(或者叫持久代)中,当方法区满的时候,会引起Full GC,所以当你大量使用动态代理时,可以将持久代设置大一些,减少Full GC次数。
引用前者:残剑
以上是关于动态代理的使用和实现机制的主要内容,如果未能解决你的问题,请参考以下文章