cglib的最初版本干了什么?
因为翻阅spring的源码,看到overrides部分,牵扯到了cglib,加上后续马上要进行的aop也要用到这个玩意儿,其实知道一些,说是用继承的方法解决代理的问题。
跟jdkproxy经常拿来比较,当然这两个也是面试经常问的问题,当然还是那句话,就木有一个技术难点经得起细看, 都是点只是没看到的点而已,然后就会被人鄙视说这都不会,只想说,去你大爷的,老子只是没看。
好了废话说多无益,来看下最初的版本吧,往往最初的版本是最核心的东西,之后的种种不过是锦上添花,那这锦才是最需要研究的。
github上cglib的版本最早的是
2002年的版本,我们把代码下载下来只有三个类。
把源码拿到后,做了个例子看下,确实比较猛。
先看下代码:
/* * Factory.java * */ package proxy.net.sf.cglib.proxy; /** * * @author user */ public interface Factory{ public Object newInstance(MethodInterceptor ih); }
public interface MethodInterceptor { /** Generated code calls this method first * @param obj this * @param method Intercepted method * @param args Arg array * @throws Throwable any exeption to stop execution * @return returned value used as parameter for all * interceptor methods */ /* public Object beforeInvoke( Object obj, java.lang.reflect.Method method, Object args[] )throws java.lang.Throwable; */ /** Generated code calls this method before invoking super * @param obj this * @param method Method * @param args Arg array * @param retValFromBefore value returned from beforeInvoke * @throws Throwable any exeption to stop execution * @return true if need to invoke super */ public boolean invokeSuper(Object obj, java.lang.reflect.Method method, Object args[] /*,Object retValFromBefore*/) throws Throwable; /** this method is invoked after execution * @param obj this * @param method Method * @param args Arg array * @param retValFromBefore value returned from beforeInvoke * @param invokedSuper value returned from invoke super * @param retValFromSuper value returner from super * @param e Exception thrown by super * @throws Throwable any exeption * @return value to return from generated method */ public Object afterReturn(Object obj, java.lang.reflect.Method method, Object args[], /*Object retValFromBefore,*/ boolean invokedSuper, Object retValFromSuper, Throwable e) throws Throwable; }
另一个就是类:Enhance.java ,这个是核心类,内容比较多,这里就不贴出来了。后边我们的分析里,主要就是Enhance里的代码在做代码增强。
下边我们来看下我们写的测试的例子:
定义一个接口
package cglib.test; public interface Call { void call(); void eat(); }
定义实例:
package cglib.test; public class Dog implements Call{ @Override public void call() { System.out.println("wang!wang!"); } @Override public void eat() { System.out.println("eat!eat!"); } }
测试类:
package cglib.test; import proxy.net.sf.cglib.proxy.Enhancer; import proxy.net.sf.cglib.proxy.MethodInterceptor; import java.lang.reflect.Method; public class CglibTest { public static void main(String args[]){ try { Dog dog = (Dog)Enhancer.enhance(Dog.class, null, new MethodInterceptor() { @Override public boolean invokeSuper(Object obj, Method method, Object[] args) throws Throwable { if(method.getName().equals("eat")){ System.out.println("before eat invoke ..."); }else if(method.getName().equals("call")){ System.out.println("before call invoke ..."); } return true; } @Override public Object afterReturn(Object obj, Method method, Object[] args, boolean invokedSuper, Object retValFromSuper, Throwable e) throws Throwable { System.out.println("after invoke ..."); return obj; } }, CglibTest.class.getClassLoader()); dog.call(); dog.eat(); } catch (Throwable throwable) { throwable.printStackTrace(); } } }
测试结果:
before call invoke ... wang!wang! after invoke ... before eat invoke ... eat!eat! after invoke ...
可以看到已经根据我们实现的MethodInteceptor进行了增强,输出我们想要的内容了。
嗯,代码拿下来后,我是新创建的工程放入代码的,发现有编译错误,查看import的错误,是缺少bcel jar包。去apache官网上下载一个相对比较老的5.2的版本。
没错就是这个包,查了下,就是用来生成增强类的。只不过后来cglib的这块儿引用被asm替换掉了,当然asm应该是比bcel更强悍的。不过既然要看最初的版本,那我们就还是按照这个版本来看吧。那就避免不了要看bcel的东西了。
花点时间跟踪下bcel的代码我们来看下最终生成的类:
生成的类名字:cglib.test.Dog$$EnhancedBySimplestore$$0
生成的类的call方法:
重点看下:21步/32步/53步
其实已经很明显了,增强之后的类,call方法调用的时候,增加了我们定义的那个MethodInteceptor的两个方法,一个在call调用前调用,一个在call调用之后调用。
至于,其他的方法我们着重看下:构造函数
构造函数写的比较明显了,这里要说明的是代码增强里,添加了属性: public MethodInteceptor h;
构造函数里,除了调用父类的构造函数外,就是把构造函数的参数里的 MethodInteceptor赋值给这个h;
翻成代码就是:
public Dog$$EnhancedBySimplestore$$0(MethodInteceptor h) {
super();
this.h = h;
}
然后,我们来看添加的另一个方法,另一个方法就是接口Factory的实现方法:
也比较简单,就是新建一个增强类的实例,然后调用它的<init>方法。然后把这个对象返回。其实就是创建增强累的实例。
翻译过来就是:
public Dog newInstance(MethodInteceptor h){
return new Dog$$EnhanceBySimplestore$$0(h).init();
}
可能翻译的不太准确,但意思就是这个意思了。
至此我们就把Enhance增强代码做的事情,基本原理弄明白了。
写的比较简单,看了一下Enhance的代码,但其实看了个大概已经花了一两个钟头的时间了,期间做的代码增强的事情,还是挺强的。其实核心在于对于Class类结构的熟知以及对Bcel的使用。有了这两样东西,加上耐心,把cglib的事情做掉就并不是难事了。
再来理解下cglib的这个最简化版本的内容,我们发现,其实我们把Enhance这个具体的实现类去除后,就是两个接口类。
那么这两个接口类就比较有意思了,一个是一个返回实例类的接口:
public Object newInstance(MethodInterceptor ih);
把MethodInteceptor实例作为参数,构造出来一个对象。
一个是具体的MethodInteceptor接口本身,这个接口有两个方法,一个是invokeSuper,一个是afterReturn。
思考一下,cglib的具体做的事情无外乎就是:
1:为增强类添加一个MethodInteceptor的属性h;
2:为增强类添加一个以MethodInteceptor接口的实例为参数的构造函数,并将MethodInteceptor的实例赋值给1步骤创建的属性h。
3:为增强类添加一个以MethodInteceptor接口的实例为参数的newInstance方法,这个方法里调用2步骤添加的构造函数,创建出增强累。
4:为原始类的方法,增加增强实现。即在调用原始方法的前后,调用属性h即传入的MethodInteceptor的实例的那两个invokeSuper和afterReturn方法。
思路,就是这么简单。把我们的MethodInteceptor的实现,增强到原始类的方法中,就可以了。借助于代码增强,和继承的特性来实现了这个方案。
解析到这里就结束了,相信cglib的后续版本做了很多的优化和其他相关功能的添加。但最核心的这个事情应该是没什么大变动的,后续有时间再专门写一篇来对比最新的版本做了那些事情。跟现在这个版本的核心代码有什么变动。