动态代理

来源:互联网 发布:fast隐藏网络 编辑:程序博客网 时间:2024/05/21 07:59

演示一个最简单的动态代理的用法,原始的逻辑是打印一句“hello world”,代理类逻辑是在原始类的方法执行前打印一句“welcome”。我们先看一下代码,然后再分析jdk是如何做到的。

public class DynamicProxyTest     interface IHello{       void sayHello();    }    static class Hello implements IHello{     public void sayHello(){      System.out.println("hello word");   }}static  class DynamicProxy implementsInvocationHandler{   Object originalObj;   Object bind(Object originalObj){        this.originalObj = orginalObj;   return Proxy.newProxyInstance(    orginalObj.getClass().getClassLoader(),originalObj.getClass().getInterfaces(),this  ); } public Object invoke(Object proxy,  Method method,Object[]args)throws Throwable{   System.out.println("welcome");   return method.invoke(originalobj,args);} public  static void main(String args[]){       IHello hello =  (IHello) new DynamicProxy().bind(new Hello());hello.sayhello(); }}

运行结果如下:

 welcome

hello world

 

上述代码中,唯一的“黑匣子”就是Proxy.newProxyInstance()方法,除此之外再没有任何特殊之处。这个方法返回一个实现了IHello的接口,并且代理了 new Hello()

实例行为的对象。跟踪这个方法的源码,可以看到程序进行了验证、优化、缓存、同步、生成字节码和显式类加载等操作。前面的步骤并不是我们关注的重点,而最后它调用了

sun.misc.ProxyGenerator.generateProxyClass()方法来挖出身材字节码的动作,这个方法可以运行时产生一个描述代理类的字节码byte[]。如果想看一看这个方法在运行时产生的代理类中写了些什么,可以在main()方法中加入下面语句:

  System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles",true);

加入这句代码后再次运行程序,磁盘中将产生一个名为"$Proxy0.class" 的代理类Class文件,反编译后可以看见如下代码:

   import  java.lang.reflect.InvocationHandler;

import  java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements DynmicProxy.IHello{

   private static Method m3;

 private static Method m1;

  private static Method m0;

 private static Method m2;

 

  public $Proxy0(InvocationHandler paramInvocationHandler){

     super(paramInvocationHandler);

 }

  public final void sayHello(){

    try{

       this.h.invoke(this,m3,null);

    }catch(){

 }

// 省略equals hashcode tostring 等方法

 }

   static{

    trh{

        m3=Class.forName("org.fenixaoft.bytecode.DynamicProxyTest$IHello").getMethod("sayHello",new Class[0]);

   .......

     }catch(){

 

    }

   }

}
 

 这个代理类的实现代码也很简单,它未传入接口中的每一个方法,以及从java.lang.Object 中继承来的 equals hashcode 等方法都生成对应的实现,

并且统一调用了InvocationHandler 对象的 invoke()方法(代码中的 ”this.h“ 就是父类Proxy中保存的InvocationHandler实例变量)来实现这些方法

的内容,各个方法的区别不过是传入的参数和Method 对象有所不同,所以无论调用动态代理的那个方法,实际上都是在执行InvocationHandler.invoke()中的

代理逻辑。

  这个例子中并没有讲到generateProxyClass()方法具体是如何产生代理类"$Proxy0.calass" 的字节码,大致的生成过程其实就是根据Class文件的格式规范

去拼装字节码,但是在实际过程中,矣byte未单位直接拼接出字节码的应用场合很少见,这种生成方式也只能产生一些高度模板化的代码,对于用户的程序

代码来说, 如果有要大量操作字节码的需求,还是使用封装好的字节码类库比较合适。如果你对动态代理的字节码拼装过程确实很感兴趣,可以到OpenJDK

的jdk/src/share/classes/sun/misc 目录下找到sun.misc.ProxyGenerator 的源码

0 0
原创粉丝点击