锁定老帖子 主题:cglib源码学习交流
该帖已经被评为精华帖
|
||
---|---|---|
作者 | 正文 | |
发表时间:2010-11-01
最后修改:2010-11-03
|
说明 个人是不太建议使用cglib动态class的方式来实现bean Immutable的模式,Immutable模式应该是一种服务接口上的显示声明,而不是如此隐晦,而且pojo bean尽量做到是轻量级,简答的set/get方法,如果要做充血的领域模型那就另当别论了。 |
reflect (class,method处理)
FastClass
顾明思义,FastClass就是对Class对象进行特定的处理,比如通过数组保存method引用,因此FastClass引出了一个index下标的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的获取method的方法。
通过数组存储method,constructor等class信息,从而将原先的反射调用,转化为class.index的直接调用,从而体现所谓的FastClass。
public class FastClassTest { public static void main(String args[]) throws Exception { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib"); FastClass clazz = FastClass.create(FastSource.class); // fast class反射调用 FastSource obj = (FastSource) clazz.newInstance(); clazz.invoke("setValue", new Class[] { int.class }, obj, new Object[] { 1 }); clazz.invoke("setOther", new Class[] { int.class }, obj, new Object[] { 2 }); int value = (Integer) clazz.invoke("getValue", new Class[] {}, obj, new Object[] {}); int other = (Integer) clazz.invoke("getOther", new Class[] {}, obj, new Object[] {}); System.out.println(value + " " + other); // fastMethod使用 FastMethod setValue = clazz.getMethod("setValue", new Class[] { int.class }); System.out.println("setValue index is : " + setValue.getIndex()); FastMethod getValue = clazz.getMethod("getValue", new Class[] {}); System.out.println("getValue index is : " + getValue.getIndex()); FastMethod setOther = clazz.getMethod("setOther", new Class[] { int.class }); System.out.println("setOther index is : " + setOther.getIndex()); FastMethod getOther = clazz.getMethod("getOther", new Class[] {}); System.out.println("getOther index is : " + getOther.getIndex()); // 其他 System.out.println("getDeclaredMethods : " + clazz.getJavaClass().getDeclaredMethods().length); System.out.println("getConstructors : " + clazz.getJavaClass().getConstructors().length); System.out.println("getFields : " + clazz.getJavaClass().getFields().length); System.out.println("getMaxIndex : " + clazz.getMaxIndex()); } } class FastSource { private int value; private int other; }
proxy (spring aop相关)
总体类结构图:
Callback & CallbackGenerator
- MethodInterceptor
- 类似于spring aop的around Advise的功能,大家都知道,不多做介绍。唯一需要注意的就是proxy.invokeSuper和proxy.invoke的区别。invokeSuper是退出当前interceptor的处理,进入下一个callback处理,invoke则会继续回调该方法,如果传递给invoke的obj参数出错容易造成递归调用
- Dispatcher, ProxyRefDispatcher
- 类似于delegate的模式,直接将请求分发给具体的Dispatcher调用,是否有着接口+实现分离的味道,将接口的方法调用通过Dispatcher转到实现target上。ProxyRefDispatcher与Dispatcher想比,loadObject()多了个当前代理对象的引用。
-
//反编译的部分代码 public final int cal(int i, int j) { CGLIB$CALLBACK_1; if(CGLIB$CALLBACK_1 != null) goto _L2; else goto _L1 _L1: JVM INSTR pop ; CGLIB$BIND_CALLBACKS(this); CGLIB$CALLBACK_1; _L2: loadObject(); //每次都进行调用 (DefaultCalcService); i; j; cal(); //调用实现类的方法 return; }
- LazyLoader
- 相比于Dispatcher,lazyLoader在第一次获取了loadObject后,会进行缓存,后续的请求调用都会直接调用该缓存的属性.
-
//反编译部分代码 public final int cal(int i, int j) { this; return ((DefaultCalcService)CGLIB$LOAD_PRIVATE_3()).cal(i, j); } private final synchronized Object CGLIB$LOAD_PRIVATE_3() { CGLIB$LAZY_LOADER_3; //保存的属性 if(CGLIB$LAZY_LOADER_3 != null) goto _L2; else goto _L1 _L1: JVM INSTR pop ; this; CGLIB$CALLBACK_3; if(CGLIB$CALLBACK_3 != null) goto _L4; else goto _L3 _L3: JVM INSTR pop ; CGLIB$BIND_CALLBACKS(this); CGLIB$CALLBACK_3; _L4: loadObject(); JVM INSTR dup_x1 ; CGLIB$LAZY_LOADER_3; _L2: return; }
- NoOp
- 不做任何处理,结合Filter针对不需要做代理方法直接返回,调用其原始方法
- FixedValue
- 强制方法返回固定值,可结合Filter进行控制
- InvocationHandler(提供和jdk proxy的功能),不常用
CallbackFilter
主要的作用就是callback调度,主要的一个方法:int accept(Method method);
返回的int在int值,代表对应method需要插入的callback,会静态生成到class的代码中,这样是cglib proxy区别于jdk proxy的方式,一个是静态的代码调用,一个是动态的reflect。
可以查看: Enhancer类中的emitMethods方法,line:883。在构造class method字节吗之前就已经确定需要运行的callback。
Enhancer
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib"); LogInteceptor logInteceptor = new LogInteceptor(); CalDispatcher calDispatcher = new CalDispatcher(); CalcProxyRefDispatcher calcProxyRefDispatcher = new CalcProxyRefDispatcher(); LazyLoaderCallback lazyLoaderCallback = new LazyLoaderCallback(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(CalcService.class); //接口类 enhancer.setCallbacks(new Callback[] { logInteceptor, calDispatcher, calcProxyRefDispatcher,lazyLoaderCallback, NoOp.INSTANCE }); // callback数组 enhancer.setCallbackFilter(new CalcCallbackFilter()); // filter CalcService service = (CalcService) enhancer.create(); int result = service.cal(1, 1);
Util (工具类,感觉有点鸡肋)
- StringSwitcher 提供string和int的map映射查询,给定一个string字符串,返回同个下标数组的int值,感觉很鸡肋,用Map不是可以很快速的实现功能
- ParallelSorter 看了具体的代码,没啥意思,就是提供了一个二分的快速排序和多路归并排序。没有所谓的并行排序,原本以为会涉及多线程处理,可惜没有
transform
暂时没仔细研究,更多的是对asm的封装,等下次看了asm代码后再回来研究下。
-----
相关性能测试和使用说明,可以参见另一篇文章: cglib相关性能测试对比
再可以来对照下spring aop中与cglib的结合,核心类: Cglib2AopProxy
看几个ProxyFactoryBean中跟cglib相关的参数: 来自于spring 1.x版本的文档描述
proxyTargetClass
:这个属性为true
时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为true
,CGLIB代理将被创建optimize
:用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理;对于JDK动态代理(缺省代理)无效。frozen
:用来控制代理工厂被配置之后,是否还允许修改通知。缺省值为false
(即在代理被配置之后,不允许修改代理的配置)。
以前一直不明白optimize和frozen的使用,正好借这次Cglib2AopProxy的学习。
ProxyCallbackFilter : 核心的callback的调度逻辑
<dt>For exposed proxies</dt>
<dd>Exposing the proxy requires code to execute before and after the method/chain invocation. This means we must use DynamicAdvisedInterceptor, since all other interceptors can avoid the need for a try/catch block</dd> <dt>For Object.finalize():</dt> <dd>No override for this method is used.</dd> <dt>For equals():</dt> <dd>The EqualsInterceptor is used to redirect equals() calls to a special handler to this proxy.</dd> <dt>For methods on the Advised class:</dt> <dd>the AdvisedDispatcher is used to dispatch the call directly to the target</dd> <dt>For advised methods:</dt> <dd>If the target is static and the advice chain is frozen then a FixedChainStaticTargetInterceptor specific to the method is used to invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is used.</dd> <dt>For non-advised methods:</dt> <dd>Where it can be determined that the method will not return <code>this</code> or when <code>ProxyFactory.getExposeProxy()</code> returns <code>false</code>, then a Dispatcher is used. For static targets, the StaticDispatcher is used; and for dynamic targets, a DynamicUnadvisedInterceptor is used. If it possible for the method to return <code>this</code> then a StaticUnadvisedInterceptor is used for static targets - the DynamicUnadvisedInterceptor already considers this.</dd> </dl>
里面针对haveAdvice和isFrozen做了些优化
1. 如果是finalize方法,直接返回NoOp,不做任何代理
2. 如果proxy对象是spring advised的子类,则通过cglib的Dispatcher,直接委托给advisor对象进行方法调用
3. 如果是equals或者hashcode方法,委托给对应的EqualsInterceptor,HashCodeInterceptor,不会走入spring的inteceptor chian。
4. 如果spring没有配置advise,则走了cglib的MethodInterceptor进行处理,直接delegate到相关proxy.method上(例如:StaticUnadvisedExposedInterceptor,DynamicUnadvisedExposedInterceptor会区分targetSource是否是单例,exposeProxy属性做相应的优化),也不会走入spring的inteceptor chian。
5. 如果spring配置了advise , 而且是isFrozen,默认值为false,这样会走到基于cglib MethodInterceptor的DynamicAdvisedInterceptor进行处理。
6. 如果spring配置了advise,还会区分是否是isFrozen=true(inteceptor chian不会动态变化), 这样会根据事先分析好的method,比如有些method不是Advisor的pointcut匹配目标,则会直接走Dispatcher进行处理,不做代理。
spring的advise的一系列inteceptor,都是每次请求动态调用advised.getInterceptorsAndDynamicInterceptionAdvice获取,然后涉及global inteceptor(.*匹配) , pointcut filter(方法匹配), inteceptor等处理。
isFrozen的功能相信大家应该能明白了,但很奇怪我没找到optimize在aop相关代码中的使用,搜了下,只看到了这么一处使用而已
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface()) { return new JdkDynamicAopProxy(config); } if (!cglibAvailable) { throw new AopConfigException( "Cannot proxy target class because CGLIB2 is not available. " + "Add CGLIB to the class path or specify proxy interfaces."); } return CglibProxyFactory.createCglibProxy(config); } else { return new JdkDynamicAopProxy(config); } }我用的spring版本为2.5.5,估计这份中文文档是spring 1.x的,难不成spring team已经做了代码优化和重构? 知道的人可以解答下
还是看看javassist吧:D
一般常用的也就是 BeanCopier, BulkBean, BeanMap, FastClass, Enhancer(MethodInterceptor)。
例子上面应该都有一部分代码,主要是针对其适用场景和不适用的场景。
简单的概括几个常用的类:
BeanCopier:适用两个Pojo Bean之间,所有属性的全复制,两边的source和target的属性可以不一致,以setter方法为准,调用getter方法获取数据。
BulkBean : 相比于BeanCopier,它可以指定特定的一组属性进行处理,然后可以调用getter,setter方法进行属性拷贝。一般会应用在动态注入属性是通过xml配置时,特别有用,拷贝特定的属性到指定的目标对象上。
BeanMap : 相比于BeanCopier和BulkBean,它可以用于解决pojo bean和map之间的转化,比如将一个map的属性赋值给一个bean上,很方便把。
FastClass : 相比于java class对象,它利用了所谓的Index[]数组进行存储method,constructor。实现method.invoke实际上是先找到FastClass存储的index下标,然后进行调用。使用上和Class没什么特殊的区别,就是多了这么一个index下标概念。
Enhancer: 一般知道个MethodInterceptor就差不多了,其他的callback可以不关注。目前直接写Enhancer应该不太多,用spring aop会比较多。
用的是jude免费版http://jude.change-vision.com/jude-web/index.html,纯java的写的,所以具有跨平台性, linux下使用比较合适。
如果windows可以用用startuml,也是免费的。
多谢支持。
对类似jvm字节码的处理学习上的确有点门槛,类似的还有javassist,BCEL,asm
毕竟文档相对少,最好是有一定的业务需求,再去理解一下它的适用场景,再结合一下反编译工具,掌握起来会相对更快
用的是jude免费版http://jude.change-vision.com/jude-web/index.html,纯java的写的,所以具有跨平台性, linux下使用比较合适。
如果windows可以用用startuml,也是免费的。
推荐一个UML工具Visual Paradigm ,社区版是免费的,也是Java写的,跨平台。
下载地址:http://www.visual-paradigm.com/download/vpuml.jsp?edition=ce
可以看下 cglib相关性能测试对比http://www.iteye.com/topic/801577
那里有一些具体的使用例子,对照例子看可能更容易理解