下面我们再来看下cglib的实现,首先看下测试类吧,上面已经看到了,主要是实现了MethodInterceptor方法,里面一个主要的类是Enhancer,那我们来看下cglib是怎么生成代理类的。
Cglib主要是这个方法
enhancer.setSuperclass(clz);//设置父类,从这个地方也可以看到cglib是通过子类化来实现代理的
enhancer.setCallback(this); //设置回调函数为this,其实只要实现了MethodInterceptor,都可以作为回调函数,不一定要是当前类
enhancer.create(); //创建代理类
设置没有什么好说的,下面我们具体看下create方法,再来一幅简图
同样的,从这幅图上,生成字节码仍然比较重要,像上面一样,举个例子,看怎么生成的,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反编译的东东
上面的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中,所有的方法都委托给了InvocationHandler的invoke方法进行处理,而toString方法也不例外,因此就会出现循环调用这种情况
Proxy的toString方法内联下就是如下的形式
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是个好东西。
上面字节码生成的东东,可以自己玩下,还是很有意思的。。。。。。
搞了这么多,你肯定看累了,我也写累了,睡了。