动态代理的使用和实现机制

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次数。

引用前者:残剑

以上是关于动态代理的使用和实现机制的主要内容,如果未能解决你的问题,请参考以下文章

Spring_AOP的实现机制-动态代理

动态代理的使用和实现机制

反射机制-动态代理

java反射机制动态代理初探

java中的动态代理机制

动态代理机制