码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-02-27 19:20:34      阅读:182      评论:0      收藏:0      [点我收藏+]

标签:.com   next   objects   throw   mil   call()   实例   type   pattern   

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

就java而言对于动态代理的支持多是以接口实现,其实现主要是通过java.lang.reflect.Proxy类,java.lang.reflect.InvocationHandler接口。Proxy类主要用于获取动态代理对象,InvocationHandler接口用来约束调用者实现。

动态代理运行机制:

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法会返回一个代理对象类的实例。当程序执行时会通过反射机制动态的生成一个代理类,该类实现一个接口里的方法(也就是说代理类与被代理类有相同的接口),在该代理类里面有一个InvocationHandler类型的成员变量,也就是调用处理程序,通过调用处理程序来给被代理类增强功能。创建好代理类后就调用类加载器将该类加载到类存,然后再通过反射创建一个该代理类的实例对象。下面是具体实现:

1.代理接口:Moveable.java

package com.test;

public interface Moveable {

       void move();

}

2.被代理对象:Tank.java
package com.test;

import java.util.Random;

public class Tank implements Moveable {

    public void move() {
         System.out.println("Tank moving...");
         try {
             Thread.sleep(new Random().nextInt(10000));
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }

}

3.为被代理对象产生一个代理类对象,其中是想增加记录运行时间的功能

package com.test;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class Proxy {
     public static Object newProxyInstance(Class interfaces,InvocationHandler h)throws Exception{
         StringBuffer methodStr = new StringBuffer();
         String tr = "\r\n";
         Method[] methods = interfaces.getMethods();
         //拼接代理类的方法
         for (Method method : methods) {
             methodStr.append(
             "    public "+ method.getReturnType()+ " " +method.getName()+"() {" + tr +
             "        try {" + tr +
             "            java.lang.reflect.Method md = " + interfaces.getName() + "." + "class.getMethod(\""  + method.getName() + "\");" + tr +
             "            h.invoke(this,md);" + tr +
             "        }catch(Exception e) {e.printStackTrace();}" + tr +
             "    }" + tr
             );
         }
        
         //拼接代理类
         String src = "package com.test;" + tr +
         "import com.test.Moveable;" + tr +
         "public class TimeProxy implements " + interfaces.getName() + " {" + tr +
         "    private com.test.InvocationHandler h;" + tr +
         "    public TimeProxy(com.test.InvocationHandler h) {" + tr +
         "        this.h = h;" + tr +
         "    }" + tr +
         methodStr.toString() + tr +
         "}";
         //创建代理类
         String fileName = System.getProperty("user.dir") + "/src/com/test/TimeProxy.java";
         File file = new File(fileName);
         FileWriter writer = new FileWriter(file);
         writer.write(src);
         writer.flush();
         writer.close();
         //编译
         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
         StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
         Iterable units = fileMgr.getJavaFileObjects(fileName);
         CompilationTask ct = compiler.getTask(null, fileMgr, null, null, null, units);
         ct.call();
         fileMgr.close();
         //加载类到内存:
         Class c = ClassLoader.getSystemClassLoader().loadClass("com.test.TimeProxy");
         Constructor constructor = c.getConstructor(InvocationHandler.class); //得到参数为InvocationHandler类型的构造方法
         Object m = constructor.newInstance(h); //通过该构造方法得到实例
         return m;
        
     }
}

4.TankProxy.java

package com.test;

import java.lang.reflect.Method;

public class TankProxy {
     public static <T> T getBean(final Object tank) throws Exception{
         return (T)Proxy.newProxyInstance(tank.getClass().getInterfaces()[0], new InvocationHandler(){
             public void invoke(Object proxy, Method method) {
                 long start = System.currentTimeMillis();
                 System.out.println("start:"+start);
                 try {
                     method.invoke(tank, new Object[]{});
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
                 long end = System.currentTimeMillis();
                 System.out.println("end:"+end);
                 System.out.println("time:"+(end-start));
             }
            
         });
     }
}

5.测试程序:

package com.test;

import java.util.List;

import com.extend.Tank2;
import com.extend.Tank3;
import com.juhe.LogProxy;
import com.juhe.TimeProxy;

public class Test {
     public static void main(String[] args) throws Exception {
         Tank tank = new Tank();
         Moveable m =  TankProxy.getBean(tank);
         m.move();
        
     }

}


执行该程序的结果为:
start:1369121253400
Tank moving...
end:1369121260078
time:6678


动态生成的代理类的内容如下:

package com.test;
import com.test.Moveable;
public class TimeProxy implements com.test.Moveable {
     private com.test.InvocationHandler h;
     public TimeProxy(com.test.InvocationHandler h) {
         this.h = h;
     }
     public void move() {
         try {
             java.lang.reflect.Method md = com.test.Moveable.class.getMethod("move");
             h.invoke(this,md);
         }catch(Exception e) {e.printStackTrace();}
     }

}

小结:动态代理在运行期通过接口动态生成代理类,这为其带来了一定的灵活性,但这个灵活性却带来了两个问题,第一代理类必须实现一个接口,如果没实现接口会抛出一个异常。第二性能影响,因为动态代理使用反射的机制实现的,首先反射肯定比直接调用要慢,其次使用反射大量生成类文件可能引起Full GC造成性能影响,因为字节码文件加载后会存放在JVM运行时区的方法区(或者叫持久代)中,当方法区满的时候,会引起Full GC,所以当你大量使用动态代理时,可以将持久代设置大一些,减少Full GC次数。

引用前者:残剑

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

标签:.com   next   objects   throw   mil   call()   实例   type   pattern   

原文地址:https://www.cnblogs.com/ldy-blogs/p/8480059.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!