上篇文章的結尾我們介紹了普通的jdk實現動態代理的主要不足在於:它只能代理實現了接口的類,如果一個類沒有繼承於任何的接口,那么就不能代理該類,原因是我們動態生成的所有代理類都必須繼承Proxy這個類,正是因為Java的單繼承,所以注定會拋棄原類型的父類。而我們的cglib通過掃描該類以及其父類中所有的public非final修飾的方法,通過asm定義該類的子類字節碼,其中該子類重寫了父類所有的方法,然后返回該子類的實例作為代理類。也就是說我們的cglib是用該類的子類作為代理類來實現代理操作的。當然cglib的缺點也是呼之欲出,對於被代理類中的非public或者final修飾的方法,不能實現代理。
在詳細介紹cglib之前,我們先簡單介紹下ASM框架,這是一個小而快的字節碼處理框架,它負責生成從被代理類中掃描出的方法的字節碼並將這些方法生成字節碼暫存在內存中。然后我們通過方法將這些字節碼轉換成class類型,最后利用反射創建代理類的實例返回。下面看一個完整的實例,稍后從源代碼的角度分析這個實例:
//定義一個接口
public interface MyInterface {
public void sayHello();
}
//定義一個ClassB類
public class ClassB {
public void welcome(){
System.out.println("welcom walker");
}
}
//模擬被代理的類,繼承了ClassB和接口MyInterface
public class ClassA extends ClassB implements MyInterface {
public void sayHello(){
System.out.println("hello walker");
}
}
//定義一個回調實例,稍后解釋
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();
}
}
輸出結果:welcom walker
我們看到,此處我們獲取了ClassA的代理對象,然后調用了ClassA父類中的welcome方法。這也間接證明了我們通過cglib代理了ClassA的父類中的方法,這一點使用jdk實現的動態處理是做不到的。下面我們解釋原理。
在這之前,由於cglib是第三方庫,所以我們需要下載相應的jar文件,主要包含兩個文件,一個是cglib的jar,還有一個是cglib依賴的ASM框架的jar文件,注意這兩個jar的版本不能沖突。
我們從main方法的主體代碼中可以看出,Enhancer 類是創建代理實例的核心類。沒錯,該類負責整個代理對象的生命周期,它就像是一個工具一樣,提供了很多方法幫助我們創建代理實例。首先我們調用了setSuperclass方法設置父類型,其實也就是將被代理對象傳入,因為我們之前說過cglib創建的代理類是原對象的子類型,所以這里稱原類型實例為父類也是合理的。
跟進去,我們看到:
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;
}
}
這段代碼的主要意思是:如果傳入的類型是接口的話,保存在專門用於保存接口類型的變量中。
private Class[] interfaces;
如果傳入的類型是Object類型的話,將用於保存普通類類型的變量賦值為null,否則保存該傳入的參數的值在該變量中。這些操作過程中保存的一些數值是為了在最后create的時候提供幫助。
接下來是setCallback方法,該方法設置了回調。也就是將來對我們代理中方法的訪問會轉發到該回調中,所有自定義的回調類必須繼承MethodInterceptor接口並實現其intercept方法,這一點和jdk的InvocationHandler類似。這里的intercept有幾個參數:
- Object obj:被代理的原對象
- Method method:被調用的當前方法
- Object[] args:該方法的參數集合
- MethodProxy proxy:被調用方法的代理,它可以和method完成同樣的事情,但是它使用FastClass機制非反射執行方法,效率高
我們對於所有調用代理方法的請求,轉發到invokeSuper方法中,該方法源碼如下:
//fastclassinfo類
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類中,幾個參數的意思解釋下,f1指向被代理對象,f2指向代理類對象,i1和i2分別是代理類中的該方法的兩個索引。也就是這種fastclass機制並不是通過反射找到指定的方法的,而是在創建代理類的時候為其中的方法建立hash索引,這樣調用的時候通過索引調用提高了效率。最后調用了代理類的方法,也就是重寫了父類的方法。
最后也是最核心的一步是create方法的調用,這個方法才是實際創建代理實例的方法,我們看源碼:
public Object create()
{
this.classOnly = false;
this.argumentTypes = null;
return createHelper();
}
該方法主要設置了兩個參數配置,指定將要創建的對象不僅僅是一個類,指定參數為空。至於這兩個參數有何作用,還需要往下追,我們看createHelper類:
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()方法主要對於一些參數進行校驗,如果不符合創建實例的標准將拋出異常,我們可以簡單的看一眼:
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");
..........
..........
.........
}
主要還是判斷回調是否指定,類型是否正確等,如果不符合創建條件就拋出異常。我們回去,接着就做了兩個判斷,用於指定被創建的代理類的名稱,我們暫時不管他。又到了一個核心的方法,該方法將創建代理類並返回該類實例。首先我們看參數都是是什么意思,就一個參數,該參數是由KEY_FACTORY.newInstance方法返回的一個Object類型,我們看到在該方法的傳入參數中,包括了父類類名或者接口名,回調類型,版本號等。該方法實際上返回了一個該代理類的一個唯一標識,這還不是關鍵,最關鍵的方法是這個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表明該代理類已經在cache中了,直接返回引用即可。否則通過 this.strategy.generate(this);方法生成該代理類的字節碼,然后通過通過類加載器加載該字節碼生成class類型,最后通過firstInstance方法生成代理類的實例返回。
最后我們看一眼剛才生成的代理的源碼:
//代碼很多,此處貼出部分
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;
}
....
....
}
從中我們看到,該類ClassA$$EnhancerByCGLIB$$64984e8e繼承自ClassA,實現了接口factory。並且在其中我們看到不僅是父類ClassA中的方法sayHello在其中被重寫了之外,ClassA的父類ClassB中的welcome方法也被重寫了。足以見得,cglib利用繼承的方式動態創建了被代理類的子類,通過ASM生成父類中所有public非final修飾的方法,實現了代理。
最后稍微小結下,cglib的實現代理的邏輯。首先我們通過Enhancer實例設置被代理類,然后設置該代理類的回調,也就是在訪問代理類方法的時候會首先轉向該回調,在回調中我們調用invokeSuper方法以fastclass這種非反射機制快速的調用到代理類中的方法,其中代理類中方法又調用原類型的對應方法。
由於cglib已經停止維護好多年,導致參考文檔很少,學習難度很大,此篇文章也是作者研讀jdk和網上優秀博文總結,不當之處,望大家指出,學習 !學習!