JDK
JDK 動態(tài)代理通過回調(diào)攔截方式忧侧,通過反射獲取模板接口名字、內(nèi)部方法以及參數(shù)播歼,再原來的接口上修改伶跷,拼接,產(chǎn)生一個新的java代理對象(類似于mybatis的反序列化代碼過程)
1.拼接java源代碼
2.編譯為class文件
3.類加載器加載新的class到內(nèi)存中
4.通過反射執(zhí)行方法(就是在invoke()那個方法)
重點:生成的代理對象不能直接調(diào)用被代理對象的方法秘狞,而是通過反射叭莫,等于說每次都得用反射調(diào)用一次,所以執(zhí)行效率不高烁试。
Cglib
Cglib動態(tài)代理采用繼承方式雇初,底層基于asm字節(jié)碼技術(shù)參數(shù)一個新的java代理對象
1.生成class文件
2.讀取class文件到內(nèi)存中
3.采用fastclass索引機制執(zhí)行方法(關(guān)鍵在于invoke()那個方法)
重點:Cglib代理實際上是通過繼承,也就是生成一個繼承被代理對象的類减响,編譯成class文件時還會額外生成一個fastclass文件靖诗,該文件記錄各個method的class索引(類名+方法名+參數(shù)),當執(zhí)行某個方法時辩蛋,通過計算索引呻畸,定位到具體的方法,代理對象執(zhí)行該方法悼院,然后super調(diào)用父類(執(zhí)行了被代理對象的方法)伤为。
生成代理對象時通過fastclass索引機制直接定位到被代理對象的class文件,從而實現(xiàn)反復調(diào)用,等于說是class復用绞愚,每次都是直接拿被代理對象的class內(nèi)容執(zhí)行的叙甸。
對比
都是需要再原來的被代理對象基礎上加額外代碼。前者的invoke內(nèi)部通過反射位衩,后者通過fastclass索引機制裆蒸,后者實現(xiàn)執(zhí)行效率更高(高于反射數(shù)倍)。
從最終生成的被代理對象來看:
jdk通過拼接糖驴,內(nèi)部只需要一個method.invoke()就可以達到目標僚祷。
cglib則需要將被代理對象(可以是多個)的class文件通過集合形式定位好,之后先執(zhí)行增強代碼贮缕,之后通過回調(diào)對象來執(zhí)行目標對象辙谜。
總結(jié):
也就是說jdk動態(tài)代理生成類速度快,調(diào)用慢感昼,cglib生成類速度慢装哆,但后續(xù)調(diào)用快,在老版本CGLIB的速度是JDK速度的10倍左右,但是CGLIB啟動類比JDK慢8倍左右,但是實際上JDK的速度在版本升級的時候每次都提高很多性能,而CGLIB仍止步不前.
在對JDK動態(tài)代理與CGlib動態(tài)代理的代碼實驗中看定嗓,1W次執(zhí)行下蜕琴,JDK7及8的動態(tài)代理性能比CGlib要好20%左右。
為何框架大多數(shù)通過接口形式宵溅?
接口形式結(jié)合多態(tài)更加方便凌简,而且接口本身沒有什么實質(zhì)性代碼,jdk 動態(tài)代理生成的對象恃逻,實際上執(zhí)行時沒有“執(zhí)行被代理方法”這一步驟号醉,而jdk生成class速度比較快(相對cglib更加輕量級),隨著jdk的版本變化對反射這一塊進行了優(yōu)化辛块,jdk更加快捷畔派。