动态代理的简单分析

动态代理的东东 听起来很牛,用起别人提供的现成的工具,也很方便,比如spring 的事务管理,虽然我们用的是和不是很多,但是真正用的时候,会有一些意想不到的东东出现,本着知其然并知其所以然的原则,我们来看下到底是怎么实现的

下面会研究下 java本身实现的动态代理,cglib实现的动态代理, 其他的比如bceljavassist实现的,大家可以自己研究下,大同小异

好了,我们开始,本着诚实的原则(第二个原则) ,下面的内容错了的都是我的,对的都是自立的J

 

首先看下两个实现是怎么写的,上代码(很烦,但是很必要),由于java是需要接口的,因此我们会有一个接口,同时会有实现,模拟一个简单的计时的动态代理

接口:public interface IHello {   public String sayHello(String ts);}

实现:public class HelloWorld implements IHello{   public  String sayHello(String ts) {  return "hello "+ts; }}

Java的实现

public class DynaHello implements InvocationHandler {

        private Object obj;

        public DynaHello(Object obj) { this.obj = obj;}

        @Override

        public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {

                long start = System.currentTimeMillis();

                Object o = method.invoke(obj, args);

                System.out.println("Call to sayHello took "+ (System.currentTimeMillis() - start) + " ms.");

                return o;

        }

}

     Cglib的实现

public class cglibProxy implements MethodInterceptor {

        private Enhancer enhancer=new Enhancer();

        public Object getProxyInstance(Class clz){

                 enhancer.setSuperclass(clz);

                 enhancer.setCallback(this);

                 return enhancer.create();

        }

        @Override

        public Object intercept(Object obj, Method method, Object[] args,                   MethodProxy proxy) throws Throwable {

                long start = System.currentTimeMillis();

                Object o = proxy.invokeSuper(obj, args);

                System.out.println("Call to sayHello took " + (System.currentTimeMillis() - start) + " ms.");

                return o;

        }

}

测试java 动态代理类

public class TestJavaDyna {

    public static void main(String[] args) {

       HelloWorld hw = new HelloWorld();

       DynaHello dh = new DynaHello(hw);

       IHello hello = (IHello) Proxy.newProxyInstance(hw.getClass().getClassLoader(), hw.getClass().getInterfaces(), dh);

       hello.sayHello("hello world");

    }

}  

 

 

下面我们来看下java是怎么实现动态代理的

首先来看Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

很简单,除去异常的处理,就三句话

Class cl = getProxyClass(loader, interfaces);//@1生成一个class

Constructor cons = cl.getConstructor(constructorParams);//@2得到这个class的构造函数

return (Object) cons.newInstance(new Object[] { h });//@3用这个构造函数,和构造函数的参数 构造一个对象 然后返回

其中constructorParams ={ InvocationHandler.class };  h就是传入的InvocationHandler的实现,这里面会在生成字节码的时候,设置进去自己的父类 java/lang/reflect/Proxy,父类有一个有参构造函数比较重要

protected Proxy(InvocationHandler h) {        this.h = h;    }  

 

下面的这幅图也许比较清晰

从上面可以看到主要还是生成代理类字节码,详细的就不讲了,举个例子讲下怎么生成对应的代理方法的,下面的就是方法生成中用到的代码

 

//1、这几句字节码的意思得到父类的一个属性,类型为InvocationHandler,此时superclassName = "java/lang/reflect/Proxy",handlerFieldName = "h";

out.writeByte(opc_getfield);   out.writeShort(cp.getFieldRef( superclassName, handlerFieldName, "Ljava/lang/reflect/InvocationHandler;"));

//2、这句话对应的字节码就是aload_0,也就是this的引用

   code_aload(0, out);

//3、这几句是得到静态属性,类型为MethodclassName= proxyPkg + $Proxy + num, proxyPkg是包名,因为接口的类型可以是public或者默认,public的时候proxyPkg就是空,默认的话,就需要代理类和接口在一个包内,包可见。

// num是自增计数器,methodFieldName就是代理类中的属性的name

out.writeByte(opc_getstatic); out.writeShort(cp.getFieldRef(dotToSlash(className),methodFieldName, "Ljava/lang/reflect/Method;"));

   //4、下面是对应方法中的参数,如果没有则是空null

   if (parameterTypes.length > 0) { code_ipush(parameterTypes.length, out);out.writeByte(opc_anewarray); out.writeShort(cp.getClass("java/lang/Object"));

           for (int i = 0; i < parameterTypes.length; i++) {

                    out.writeByte(opc_dup);

                    code_ipush(i, out);

                    codeWrapArgument(parameterTypes[i], parameterSlot[i], out);

                    out.writeByte(opc_aastore);

                }

            } else {

                out.writeByte(opc_aconst_null);

            }

   //5、下面两句话是调用接口,第一个参数为上面loadthis,第二个参数为对应Method类型的方法,第三个参数就是上面组装的方法中的参数

   out.writeByte(opc_invokeinterface);

out.writeShort(cp.getInterfaceMethodRef("java/lang/reflect/InvocationHandler","invoke","(Ljava/lang/Object;Ljava/lang/reflect/Method;" +"[Ljava/lang/Object;)Ljava/lang/Object;"))

这个太枯燥了,下面我们用jd_gui看下生成好的字节码是什么样的,挑个sayHello方法吧,除去各种异常处理,方法如下:

public final String sayHello(){     return ((String)this.h.invoke(this, m3, new Object[] { paramString })); }

其中h就是父类Proxy中的属性,m3 = Class.forName("javaDyna.IHello").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });也就是m3是接口中的方法

这个里面有个地方 sayhello没有参数了,这个是jd的问题,请大家不要纠结在这个地方。

 

 

通过这一个方法,就可以知道,java动态代理是将所有的方法都委托给了InvocationHandler实现中的invoke方法

了,

看着很简单,呵呵,但是这里面的一些异常,以及缓存的处理还是值得学些的,当然我们只看主干,就行了哈哈

上面就将生成字节码说完了,最后通过 InvocationHandler的实现传入构造函数,就完成了代理对象的生成,

那时的h才真正是实现。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值