?????上篇文章的結(jié)尾我們介紹了普通的jdk實(shí)現(xiàn)動(dòng)態(tài)代理的主要不足在于:它只能代理實(shí)現(xiàn)了接口的類(lèi)辰妙,如果一個(gè)類(lèi)沒(méi)有繼承于任何的接口压状,那么就不能代理該類(lèi)仆抵,原因是我們動(dòng)態(tài)生成的所有代理類(lèi)都必須繼承Proxy這個(gè)類(lèi),正是因?yàn)镴ava的單繼承种冬,所以注定會(huì)拋棄原類(lèi)型的父類(lèi)镣丑。而我們的cglib通過(guò)掃描該類(lèi)以及其父類(lèi)中所有的public非final修飾的方法,通過(guò)asm定義該類(lèi)的子類(lèi)字節(jié)碼娱两,其中該子類(lèi)重寫(xiě)了父類(lèi)所有的方法莺匠,然后返回該子類(lèi)的實(shí)例作為代理類(lèi)。也就是說(shuō)我們的cglib是用該類(lèi)的子類(lèi)作為代理類(lèi)來(lái)實(shí)現(xiàn)代理操作的十兢。當(dāng)然cglib的缺點(diǎn)也是呼之欲出部蛇,對(duì)于被代理類(lèi)中的非public或者final修飾的方法堪伍,不能實(shí)現(xiàn)代理纱控。
?????在詳細(xì)介紹cglib之前梳星,我們先簡(jiǎn)單介紹下ASM框架,這是一個(gè)小而快的字節(jié)碼處理框架异袄,它負(fù)責(zé)生成從被代理類(lèi)中掃描出的方法的字節(jié)碼并將這些方法生成字節(jié)碼暫存在內(nèi)存中通砍。然后我們通過(guò)方法將這些字節(jié)碼轉(zhuǎn)換成class類(lèi)型,最后利用反射創(chuàng)建代理類(lèi)的實(shí)例返回烤蜕。下面看一個(gè)完整的實(shí)例封孙,稍后從源代碼的角度分析這個(gè)實(shí)例:
//定義一個(gè)接口
public interface MyInterface {
public void sayHello();
}
//定義一個(gè)ClassB類(lèi)
public class ClassB {
public void welcome(){
System.out.println("welcom walker");
}
}
//模擬被代理的類(lèi),繼承了ClassB和接口MyInterface
public class ClassA extends ClassB implements MyInterface {
public void sayHello(){
System.out.println("hello walker");
}
}
//定義一個(gè)回調(diào)實(shí)例讽营,稍后解釋
public class MyMethod implements MethodInterceptor {
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable{
proxy.invokeSuper(obj, args);
return null;
}
}
public class Test {
public static void main(String[] args) throws Exception {
ClassA ca = new ClassA();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ClassA.class);
enhancer.setCallback(new MyMethod());
ClassA my = (ClassA)enhancer.create();
my.welcome();
}
}
輸出結(jié)果:welcom walker
我們看到虎忌,此處我們獲取了ClassA的代理對(duì)象,然后調(diào)用了ClassA父類(lèi)中的welcome方法橱鹏。這也間接證明了我們通過(guò)cglib代理了ClassA的父類(lèi)中的方法膜蠢,這一點(diǎn)使用jdk實(shí)現(xiàn)的動(dòng)態(tài)處理是做不到的堪藐。下面我們解釋原理。
在這之前挑围,由于cglib是第三方庫(kù)礁竞,所以我們需要下載相應(yīng)的jar文件,主要包含兩個(gè)文件杉辙,一個(gè)是cglib的jar模捂,還有一個(gè)是cglib依賴(lài)的ASM框架的jar文件,注意這兩個(gè)jar的版本不能沖突蜘矢。
我們從main方法的主體代碼中可以看出狂男,Enhancer 類(lèi)是創(chuàng)建代理實(shí)例的核心類(lèi)。沒(méi)錯(cuò)品腹,該類(lèi)負(fù)責(zé)整個(gè)代理對(duì)象的生命周期岖食,它就像是一個(gè)工具一樣,提供了很多方法幫助我們創(chuàng)建代理實(shí)例珍昨。首先我們調(diào)用了setSuperclass方法設(shè)置父類(lèi)型,其實(shí)也就是將被代理對(duì)象傳入句喷,因?yàn)槲覀冎罢f(shuō)過(guò)cglib創(chuàng)建的代理類(lèi)是原對(duì)象的子類(lèi)型镣典,所以這里稱(chēng)原類(lèi)型實(shí)例為父類(lèi)也是合理的。
跟進(jìn)去唾琼,我們看到:
public void setSuperclass(Class superclass)
{
if ((superclass != null) && (superclass.isInterface())) {
setInterfaces(new Class[] { superclass });
} else if ((superclass != null) && (superclass.equals(Object.class))) {
this.superclass = null;
} else {
this.superclass = superclass;
}
}
這段代碼的主要意思是:如果傳入的類(lèi)型是接口的話(huà)兄春,保存在專(zhuān)門(mén)用于保存接口類(lèi)型的變量中。
private Class[] interfaces;
如果傳入的類(lèi)型是Object類(lèi)型的話(huà)锡溯,將用于保存普通類(lèi)類(lèi)型的變量賦值為null赶舆,否則保存該傳入的參數(shù)的值在該變量中。這些操作過(guò)程中保存的一些數(shù)值是為了在最后create的時(shí)候提供幫助祭饭。
接下來(lái)是setCallback方法芜茵,該方法設(shè)置了回調(diào)。也就是將來(lái)對(duì)我們代理中方法的訪(fǎng)問(wèn)會(huì)轉(zhuǎn)發(fā)到該回調(diào)中倡蝙,所有自定義的回調(diào)類(lèi)必須繼承MethodInterceptor接口并實(shí)現(xiàn)其intercept方法九串,這一點(diǎn)和jdk的InvocationHandler類(lèi)似。這里的intercept有幾個(gè)參數(shù):
- Object obj:被代理的原對(duì)象
- Method method:被調(diào)用的當(dāng)前方法
- Object[] args:該方法的參數(shù)集合
- MethodProxy proxy:被調(diào)用方法的代理寺鸥,它可以和method完成同樣的事情猪钮,但是它使用FastClass機(jī)制非反射執(zhí)行方法,效率高
我們對(duì)于所有調(diào)用代理方法的請(qǐng)求胆建,轉(zhuǎn)發(fā)到invokeSuper方法中烤低,該方法源碼如下:
//fastclassinfo類(lèi)
private static class FastClassInfo
{
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {}
FastClassInfo(MethodProxy.1 x0)
{
this();
}
}
public Object invokeSuper(Object obj, Object[] args)
throws Throwable
{
try
{
init();
FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
}
catch (InvocationTargetException e)
{
throw e.getTargetException();
}
}
其中fastclassinfo類(lèi)中,幾個(gè)參數(shù)的意思解釋下笆载,f1指向被代理對(duì)象扑馁,f2指向代理類(lèi)對(duì)象涯呻,i1和i2分別是代理類(lèi)中的該方法的兩個(gè)索引。也就是這種fastclass機(jī)制并不是通過(guò)反射找到指定的方法的檐蚜,而是在創(chuàng)建代理類(lèi)的時(shí)候?yàn)槠渲械姆椒ńash索引魄懂,這樣調(diào)用的時(shí)候通過(guò)索引調(diào)用提高了效率。最后調(diào)用了代理類(lèi)的方法闯第,也就是重寫(xiě)了父類(lèi)的方法市栗。
最后也是最核心的一步是create方法的調(diào)用,這個(gè)方法才是實(shí)際創(chuàng)建代理實(shí)例的方法咳短,我們看源碼:
public Object create()
{
this.classOnly = false;
this.argumentTypes = null;
return createHelper();
}
該方法主要設(shè)置了兩個(gè)參數(shù)配置填帽,指定將要?jiǎng)?chuàng)建的對(duì)象不僅僅是一個(gè)類(lèi),指定參數(shù)為空咙好。至于這兩個(gè)參數(shù)有何作用篡腌,還需要往下追,我們看createHelper類(lèi):
private Object createHelper()
{
validate();
if (this.superclass != null) {
setNamePrefix(this.superclass.getName());
} else if (this.interfaces != null) {
setNamePrefix(this.interfaces[ReflectUtils.findPackageProtected(this.interfaces)].getName());
}
return super.create(KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter, this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID));
}
validate()方法主要對(duì)于一些參數(shù)進(jìn)行校驗(yàn)勾效,如果不符合創(chuàng)建實(shí)例的標(biāo)準(zhǔn)將拋出異常嘹悼,我們可以簡(jiǎn)單的看一眼:
private void validate()
{
if ((this.classOnly ^ this.callbacks == null))
{
if (this.classOnly) {
throw new IllegalStateException("createClass does not accept callbacks");
}
throw new IllegalStateException("Callbacks are required");
}
if ((this.classOnly) && (this.callbackTypes == null)) {
throw new IllegalStateException("Callback types are required");
}
if ((this.callbacks != null) && (this.callbackTypes != null))
{
if (this.callbacks.length != this.callbackTypes.length) {
throw new IllegalStateException("Lengths of callback and callback types array must be the same");
..........
..........
.........
}
主要還是判斷回調(diào)是否指定,類(lèi)型是否正確等层宫,如果不符合創(chuàng)建條件就拋出異常杨伙。我們回去,接著就做了兩個(gè)判斷萌腿,用于指定被創(chuàng)建的代理類(lèi)的名稱(chēng)限匣,我們暫時(shí)不管他。又到了一個(gè)核心的方法毁菱,該方法將創(chuàng)建代理類(lèi)并返回該類(lèi)實(shí)例米死。首先我們看參數(shù)都是是什么意思,就一個(gè)參數(shù)贮庞,該參數(shù)是由KEY_FACTORY.newInstance方法返回的一個(gè)Object類(lèi)型峦筒,我們看到在該方法的傳入?yún)?shù)中,包括了父類(lèi)類(lèi)名或者接口名窗慎,回調(diào)類(lèi)型勘天,版本號(hào)等。該方法實(shí)際上返回了一個(gè)該代理類(lèi)的一個(gè)唯一標(biāo)識(shí)捉邢,這還不是關(guān)鍵脯丝,最關(guān)鍵的方法是這個(gè)create方法:
protected Object create(Object key)
{
try
{
Class gen = null;
synchronized (this.source)
{
ClassLoader loader = getClassLoader();
Map cache2 = null;
cache2 = (Map)this.source.cache.get(loader);
if (cache2 == null)
{
cache2 = new HashMap();
cache2.put(NAME_KEY, new HashSet());
this.source.cache.put(loader, cache2);
}
else if (this.useCache)
{
Reference ref = (Reference)cache2.get(key);
gen = (Class)(ref == null ? null : ref.get());
}
if (gen == null)
{
Object save = CURRENT.get();
CURRENT.set(this);
try
{
this.key = key;
if (this.attemptLoad) {
try
{
gen = loader.loadClass(getClassName());
}
catch (ClassNotFoundException e) {}
}
if (gen == null)
{
b = this.strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
getClassNameCache(loader).add(className);
gen = ReflectUtils.defineClass(className, b, loader);
}
if (this.useCache) {
cache2.put(key, new WeakReference(gen));
}
byte[] b = firstInstance(gen);
CURRENT.set(save);return b;
}
finally
{
CURRENT.set(save);
}
}
}
return firstInstance(gen);
//省去了異常捕獲的代碼塊
如果usecache為為true表明該代理類(lèi)已經(jīng)在cache中了,直接返回引用即可伏伐。否則通過(guò) this.strategy.generate(this);方法生成該代理類(lèi)的字節(jié)碼宠进,然后通過(guò)通過(guò)類(lèi)加載器加載該字節(jié)碼生成class類(lèi)型,最后通過(guò)firstInstance方法生成代理類(lèi)的實(shí)例返回藐翎。
最后我們看一眼剛才生成的代理的源碼:
//代碼很多材蹬,此處貼出部分
public class ClassA$$EnhancerByCGLIB$$64984e8e extends ClassA
implements Factory
{
final void CGLIB$sayHello$0()
{
super.sayHello();
}
public final void sayHello()
{
CGLIB$CALLBACK_0;
if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
JVM INSTR pop ;
CGLIB$BIND_CALLBACKS(this);
CGLIB$CALLBACK_0;
_L2:
JVM INSTR dup ;
JVM INSTR ifnull 37;
goto _L3 _L4
_L3:
break MISSING_BLOCK_LABEL_21;
_L4:
break MISSING_BLOCK_LABEL_37;
this;
CGLIB$sayHello$0$Method;
CGLIB$emptyArgs;
CGLIB$sayHello$0$Proxy;
intercept();
return;
super.sayHello();
return;
}
final void CGLIB$welcome$1()
{
super.welcome();
}
public final void welcome()
{
CGLIB$CALLBACK_0;
if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
JVM INSTR pop ;
CGLIB$BIND_CALLBACKS(this);
CGLIB$CALLBACK_0;
_L2:
JVM INSTR dup ;
JVM INSTR ifnull 37;
goto _L3 _L4
_L3:
break MISSING_BLOCK_LABEL_21;
_L4:
break MISSING_BLOCK_LABEL_37;
this;
CGLIB$welcome$1$Method;
CGLIB$emptyArgs;
CGLIB$welcome$1$Proxy;
intercept();
return;
super.welcome();
return;
}
....
....
}
從中我們看到实幕,該類(lèi)ClassA$$EnhancerByCGLIB$$64984e8e繼承自ClassA,實(shí)現(xiàn)了接口factory堤器。并且在其中我們看到不僅是父類(lèi)ClassA中的方法sayHello在其中被重寫(xiě)了之外昆庇,ClassA的父類(lèi)ClassB中的welcome方法也被重寫(xiě)了。足以見(jiàn)得闸溃,cglib利用繼承的方式動(dòng)態(tài)創(chuàng)建了被代理類(lèi)的子類(lèi)整吆,通過(guò)ASM生成父類(lèi)中所有public非final修飾的方法,實(shí)現(xiàn)了代理辉川。
最后稍微小結(jié)下表蝙,cglib的實(shí)現(xiàn)代理的邏輯。首先我們通過(guò)Enhancer實(shí)例設(shè)置被代理類(lèi)乓旗,然后設(shè)置該代理類(lèi)的回調(diào)府蛇,也就是在訪(fǎng)問(wèn)代理類(lèi)方法的時(shí)候會(huì)首先轉(zhuǎn)向該回調(diào),在回調(diào)中我們調(diào)用invokeSuper方法以fastclass這種非反射機(jī)制快速的調(diào)用到代理類(lèi)中的方法屿愚,其中代理類(lèi)中方法又調(diào)用原類(lèi)型的對(duì)應(yīng)方法汇跨。
由于cglib已經(jīng)停止維護(hù)好多年,導(dǎo)致參考文檔很少妆距,學(xué)習(xí)難度很大穷遂,此篇文章也是作者研讀jdk和網(wǎng)上優(yōu)秀博文總結(jié),不當(dāng)之處毅厚,望大家指出塞颁,學(xué)習(xí) 浦箱!學(xué)習(xí)吸耿!