您的位置:首页 > 其它

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

2018-02-27 17:32 155 查看

工作中很久没有接触动态代理,之前的学习也有些模糊,导致有些遗忘,这里记录下个人对动态代理的理解,如有读者发现问题多多指正吧。

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

引用前者:残剑

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: