- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Java cglib動(dòng)態(tài)代理原理分析
本文分下面三個(gè)部分來(lái)分析cglib動(dòng)態(tài)代理的原理。
public class Target{ public void f(){ System.out.println("Target f()"); } public void g(){ System.out.println("Target g()"); } } public class Interceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("I am intercept begin"); //Note: 此處一定要使用proxy的invokeSuper方法來(lái)調用目標類(lèi)的方法 proxy.invokeSuper(obj, args); System.out.println("I am intercept end"); return null; } } public class Test { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code"); //實(shí)例化一個(gè)增強器,也就是cglib中的一個(gè)class generator Enhancer eh = new Enhancer(); //設置目標類(lèi) eh.setSuperclass(Target.class); // 設置攔截對象 eh.setCallback(new Interceptor()); // 生成代理類(lèi)并返回一個(gè)實(shí)例 Target t = (Target) eh.create(); t.f(); t.g(); } }
運行結果為:
I am intercept begin
Target f()
I am intercept end
I am intercept begin
Target g()
I am intercept end
與JDK動(dòng)態(tài)代理相比,cglib可以實(shí)現對一般類(lèi)的代理而無(wú)需實(shí)現接口。在上例中通過(guò)下列步驟來(lái)生成目標類(lèi)Target的代理類(lèi):
在示例代碼中我們通過(guò)設置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY的屬性值來(lái)獲取cglib生成的代理類(lèi)。通過(guò)之前分析的命名規則我們可以很容易的在F:\\code下面找到生成的代理類(lèi) Target$$EnhancerByCGLIB$$788444a0.class 。
使用jd-gui進(jìn)行反編譯(由于版本的問(wèn)題,此處只能顯示部分代碼,可以結合javap的反編譯結果來(lái)進(jìn)行分析),由于cglib會(huì )代理Object中的finalize,equals, toString,hashCode,clone方法,為了清晰的展示代理類(lèi)我們省略這部分代碼,反編譯的結果如下:
public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory { private Boolean CGLIB$BOUND; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static final Method CGLIB$g$0$Method; private static final MethodProxy CGLIB$g$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$f$1$Method; private static final MethodProxy CGLIB$f$1$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0"); Class localClass2; Method[] tmp60_57 = ReflectUtils.findMethods(new String[] { "g", "()V", "f", "()V" }, (localClass2 = Class.forName("net.sf.cglib.test.Target")).getDeclaredMethods()); CGLIB$g$0$Method = tmp60_57[0]; CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0"); CGLIB$f$1$Method = tmp60_57[1]; CGLIB$f$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "f", "CGLIB$f$1"); } final void CGLIB$g$0() { super.g(); } public final void g() { MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null) { CGLIB$BIND_CALLBACKS(this); tmp4_1 = this.CGLIB$CALLBACK_0; } if (this.CGLIB$CALLBACK_0 != null) { tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy); } else{ super.g(); } } }
代理類(lèi)(Target$$EnhancerByCGLIB$$788444a0)繼承了目標類(lèi)(Target),至于代理類(lèi)實(shí)現的factory接口與本文無(wú)關(guān),殘忍無(wú)視。代理類(lèi)為每個(gè)目標類(lèi)的方法生成兩個(gè)方法,例如針對目標類(lèi)中的每個(gè)非private方法,代理類(lèi)會(huì )生成兩個(gè)方法,以g方法為例:一個(gè)是@Override的g方法,一個(gè)是CGLIB$g$0(CGLIB$g$0相當于目標類(lèi)的g方法)。我們在示例代碼中調用目標類(lèi)的方法t.g()時(shí),實(shí)際上調用的是代理類(lèi)中的g()方法。接下來(lái)我們著(zhù)重分析代理類(lèi)中的g方法,看看是怎么實(shí)現的代理功能。
當調用代理類(lèi)的g方法時(shí),先判斷是否已經(jīng)存在實(shí)現了MethodInterceptor接口的攔截對象,如果沒(méi)有的話(huà)就調用CGLIB$BIND_CALLBACKS方法來(lái)獲取攔截對象,CGLIB$BIND_CALLBACKS的反編譯結果如下:
private static final void CGLIB$BIND_CALLBACKS(java.lang.Object); Code: 0: aload_0 1: checkcast #2; //class net/sf/cglib/test/Target$$EnhancerByCGLIB$$788444a0 4: astore_1 5: aload_1 6: getfield #212; //Field CGLIB$BOUND:Z 9: ifne 52 12: aload_1 13: iconst_1 14: putfield #212; //Field CGLIB$BOUND:Z 17: getstatic #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal; 20: invokevirtual #215; //Method java/lang/ThreadLocal.get:()Ljava/lang/Object; 23: dup 24: ifnonnull 39 27: pop 28: getstatic #210; //Field CGLIB$STATIC_CALLBACKS:[Lnet/sf/cglib/proxy/Callback; 31: dup 32: ifnonnull 39 35: pop 36: goto 52 39: checkcast #216; //class "[Lnet/sf/cglib/proxy/Callback;" 42: aload_1 43: swap 44: iconst_0 45: aaload 46: checkcast #48; //class net/sf/cglib/proxy/MethodInterceptor 49: putfield #36; //Field CGLIB$CALLBACK_0:Lnet/sf/cglib/proxy/MethodInterceptor; 52: return
為了方便閱讀,等價(jià)的代碼如下:
private static final void CGLIB$BIND_CALLBACKS(Object o){ Target$$EnhancerByCGLIB$$788444a0 temp_1 = (Target$$EnhancerByCGLIB$$788444a0)o; Object temp_2; Callback[] temp_3 if(temp_1.CGLIB$BOUND == true){ return; } temp_1.CGLIB$BOUND = true; temp_2 = CGLIB$THREAD_CALLBACKS.get(); if(temp_2!=null){ temp_3 = (Callback[])temp_2; } else if(CGLIB$STATIC_CALLBACKS!=null){ temp_3 = CGLIB$STATIC_CALLBACKS; } else{ return; } temp_1.CGLIB$CALLBACK_0 = (MethodInterceptor)temp_3[0]; return; }
CGLIB$BIND_CALLBACKS 先從CGLIB$THREAD_CALLBACKS中g(shù)et攔截對象,如果獲取不到的話(huà),再從CGLIB$STATIC_CALLBACKS來(lái)獲取,如果也沒(méi)有則認為該方法不需要代理。
那么攔截對象是如何設置到CGLIB$THREAD_CALLBACKS 或者 CGLIB$STATIC_CALLBACKS中的呢?
在Jdk動(dòng)態(tài)代理中攔截對象是在實(shí)例化代理類(lèi)時(shí)由構造函數傳入的,在cglib中是調用Enhancer的firstInstance方法來(lái)生成代理類(lèi)實(shí)例并設置攔截對象的。firstInstance的調用軌跡為:
在第5步,調用了代理類(lèi)的CGLIB$SET_THREAD_CALLBACKS來(lái)完成攔截對象的注入。下面我們看一下CGLIB$SET_THREAD_CALLBACKS的反編譯結果:
public static void CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]); Code: 0: getstatic #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal; 3: aload_0 4: invokevirtual #207; //Method java/lang/ThreadLocal.set:(Ljava/lang/Object;)V 7: return
在CGLIB$SET_THREAD_CALLBACKS方法中調用了CGLIB$THREAD_CALLBACKS的set方法來(lái)保存攔截對象,在CGLIB$BIND_CALLBACKS方法中使用了CGLIB$THREAD_CALLBACKS的get方法來(lái)獲取攔截對象,并保存到CGLIB$CALLBACK_0中。這樣,在我們調用代理類(lèi)的g方法時(shí),就可以獲取到我們設置的攔截對象,然后通過(guò) tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy) 來(lái)實(shí)現代理。這里來(lái)解釋一下intercept方法的參數含義:
@para1 obj :代理對象本身
@para2 method : 被攔截的方法對象
@para3 args:方法調用入參
@para4 proxy:用于調用被攔截方法的方法代理對象
這里會(huì )有一個(gè)疑問(wèn),為什么不直接反射調用代理類(lèi)生成的(CGLIB$g$0)來(lái)間接調用目標類(lèi)的被攔截方法,而使用proxy的invokeSuper方法呢?這里就涉及到了另外一個(gè)點(diǎn)— FastClass 。
Jdk動(dòng)態(tài)代理的攔截對象是通過(guò)反射的機制來(lái)調用被攔截方法的,反射的效率比較低,所以cglib采用了FastClass的機制來(lái)實(shí)現對被攔截方法的調用。FastClass機制就是對一個(gè)類(lèi)的方法建立索引,通過(guò)索引來(lái)直接調用相應的方法,下面用一個(gè)小例子來(lái)說(shuō)明一下,這樣比較直觀(guān)
public class test10 { public static void main(String[] args){ Test tt = new Test(); Test2 fc = new Test2(); int index = fc.getIndex("f()V"); fc.invoke(index, tt, null); } } class Test{ public void f(){ System.out.println("f method"); } public void g(){ System.out.println("g method"); } } class Test2{ public Object invoke(int index, Object o, Object[] ol){ Test t = (Test) o; switch(index){ case 1: t.f(); return null; case 2: t.g(); return null; } return null; } public int getIndex(String signature){ switch(signature.hashCode()){ case 3078479: return 1; case 3108270: return 2; } return -1; } }
上例中,Test2是Test的Fastclass,在Test2中有兩個(gè)方法getIndex和invoke。在getIndex方法中對Test的每個(gè)方法建立索引,并根據入參(方法名+方法的描述符)來(lái)返回相應的索引。Invoke根據指定的索引,以ol為入參調用對象O的方法。這樣就避免了反射調用,提高了效率。代理類(lèi)(Target$$EnhancerByCGLIB$$788444a0)中與生成Fastclass相關(guān)的代碼如下:
Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0"); localClass2 = Class.forName("net.sf.cglib.test.Target"); CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");
MethodProxy中會(huì )對localClass1和localClass2進(jìn)行分析并生成FastClass,然后再使用getIndex來(lái)獲取方法g 和 CGLIB$g$0的索引,具體的生成過(guò)程將在后續進(jìn)行介紹,這里介紹一個(gè)關(guān)鍵的內部類(lèi):
private static class FastClassInfo { FastClass f1; // net.sf.cglib.test.Target的fastclass FastClass f2; // Target$$EnhancerByCGLIB$$788444a0 的fastclass int i1; //方法g在f1中的索引 int i2; //方法CGLIB$g$0在f2中的索引 }
MethodProxy 中invokeSuper方法的代碼如下:
FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args);
當調用invokeSuper方法時(shí),實(shí)際上是調用代理類(lèi)的CGLIB$g$0方法,CGLIB$g$0直接調用了目標類(lèi)的g方法。所以,在第一節示例代碼中我們使用invokeSuper方法來(lái)調用被攔截的目標類(lèi)方法。
至此,我們已經(jīng)了解cglib動(dòng)態(tài)代理的工作原理,接下來(lái)會(huì )對cglib的相關(guān)源碼進(jìn)行分析。
以上就是Java cglib動(dòng)態(tài)代理原理分析的詳細內容,更多關(guān)于Java cglib動(dòng)態(tài)代理原理的資料請關(guān)注腳本之家其它相關(guān)文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站