动态代理的简单分析(2)

下面我们再来看下cglib的实现,首先看下测试类吧,上面已经看到了,主要是实现了MethodInterceptor方法,里面一个主要的类是Enhancer,那我们来看下cglib是怎么生成代理类的。

Cglib主要是这个方法

enhancer.setSuperclass(clz);//设置父类,从这个地方也可以看到cglib是通过子类化来实现代理的

    enhancer.setCallback(this); //设置回调函数为this,其实只要实现了MethodInterceptor,都可以作为回调函数,不一定要是当前类

    enhancer.create();          //创建代理类

设置没有什么好说的,下面我们具体看下create方法,再来一幅简图

动态代理的简单分析(2)

同样的,从这幅图上,生成字节码仍然比较重要,像上面一样,举个例子,看怎么生成的,cglib使用asm作为生成字节码的基础类库,比较麻烦和啰嗦,见重点的写一些

//1开始写方法体内容,首先是判断是否为空

            e = context.beginMethod(ce, method);    Label nullInterceptor = e.make_label();            context.emitCallback(e, context.getIndex(method));            e.dup();            e.ifnull(nullInterceptor);

//2、得到相应方法的method,这个method是指被代理类的method,在代理类中是一个属性

            e.load_this();            e.getfield(methodField);            if (method.getSignature().getArgumentTypes().length == 0) {                e.getfield(EMPTY_ARGS_NAME);            } else {                e.create_arg_array();            }

//3、得到相应方法代理类的method,在代理类中是一个属性

            e.getfield(methodProxyField);

//4、调用interceptor的方法进行代理,此时的METHOD_INTERCEPTOR =TypeUtils.parseType("net.sf.cglib.proxy.MethodInterceptor")INTERCEPT = new Signature()

            e.invoke_interface(METHOD_INTERCEPTOR, INTERCEPT);

上面太枯燥,直接看jd反编译的东东

 动态代理的简单分析(2)

上面的CGLIB$CALLBACK_0,就是我们要传入的MethodInterceptor接口,两个参数就是学问了

GLIB$sayHello$0$Proxy = MethodProxy.create(tmp27_17, (498ec6d0.CGLIB$sayHello$0$Method = Class.forName("com.HelloWorld").getDeclaredMethod("sayHello", new Class[] { Class.forName("java.lang.String") })).getDeclaringClass(), localClass, "(Ljava/lang/String;)Ljava/lang/String;", "sayHello", "CGLIB$sayHello$0");

比较长,总的意思就是CGLIB$sayHello$0$Method 这个就是被代理类的方法也就是sayhello方法,CGLIB$sayHello$0$Proxy就是代理类的方法,这个方法用asm完全重写,没有用反射。

 

上面生成完之后,最后将那个MethodInterceptor的实现设置进去,就实例化为了一个代理对象

从上面可以看到,代理类的实现很简单,就是讲所有的方法,用你的实现方法全部替换下,所以如果比较细心和勇于尝试的话,自己都可以实现代理框架,

一些因为代理产生的问题,如果能明白里面参数的构成和原理,就会一目了然

比如在java的实现中,你要看下第一个参数是什么类型的,真的是Proxy的子类么?

publicclass 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 {

                System.out.println(proxy);

                long start = System.currentTimeMillis();

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

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

                return o;

        }

}

如果用上面的代码作为InvocationHandler实现,在执行的时候就会报错,大家就会知道为什么了。

原因是:在代理类也就是proxy中,所有的方法都委托给了InvocationHandlerinvoke方法进行处理,而toString方法也不例外,因此就会出现循环调用这种情况

ProxytoString方法内联下就是如下的形式

public String toString() {

                System.out.println(proxy.toString());//明显的循环调用了,所以跑错

                long start = System.currentTimeMillis();

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

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

                return o;

    }

其他的就不说了,自己体会,肯定会有人想突破java动态代理,必须实现接口的限制  这篇文章满足你的求知欲https://www.ibm.com/developerworks/cn/java/j-lo-proxy2/

另外fastjson为什么说自己的序列化会很快,并且序列化的结果比较小,就是因为用了asm,针对每一个pojo写出特定化的序列化类,这样就会比那种通用的序列化类快一些,所以说asm是个好东西。

上面字节码生成的东东,可以自己玩下,还是很有意思的。。。。。。

搞了这么多,你肯定看累了,我也写累了,睡了。