动态代理

演示一个最简单的动态代理的用法,原始的逻辑是打印一句“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 implements
InvocationHandler{
   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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值