Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解
Cglib動(dòng)態(tài)代理
我們先通過一個(gè)demo看一下Cglib是如何實(shí)現(xiàn)動(dòng)態(tài)代理的。
首先定義個(gè)服務(wù)類,有兩個(gè)方法并且其中一個(gè)方法用final來修飾。
public class PersonService {
public PersonService() {
System.out.println("PersonService構(gòu)造");
}
//該方法不能被子類覆蓋
final public Person getPerson(String code) {
System.out.println("PersonService:getPerson>>"+code);
return null;
}
public void setPerson() {
System.out.println("PersonService:setPerson");
}
}Cglib是無法代理final修飾的方法的,具體原因我們一會(huì)通過源碼來分析。
然后,定義一個(gè)自定義MethodInterceptor。
public class CglibProxyIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("執(zhí)行前...");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("執(zhí)行后...");
return object;
}
}
我們看一下intercept方法入?yún)ⅲ瑂ub:cglib生成的代理對(duì)象,method:被代理對(duì)象方法,objects:方法入?yún)?,methodProxy:代理方法
最后,我們寫個(gè)例子調(diào)用一下,并將Cglib生成的代理類class文件輸出磁盤方便我們反編譯查看源碼。
public class Test {
public static void main(String[] args) { //代理類class文件存入本地磁盤
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback(new CglibProxyIntercepter());
PersonService proxy= (PersonService) enhancer.create();
proxy.setPerson();
proxy.getPerson("1");
}
}我們執(zhí)行一下會(huì)發(fā)現(xiàn)getPerson因?yàn)榧觙inal修飾并沒有被代理,下面我們通過源碼分析一下。
執(zhí)行前... PersonService:setPerson 執(zhí)行后... PersonService:getPerson>>1
生成代理類 執(zhí)行Test測(cè)試類可以得到Cglib生成的class文件,一共有三個(gè)class文件我們反編譯以后逐個(gè)說一下他們的作用。

PersonService$$EnhancerByCGLIB$$eaaaed75就是cglib生成的代理類,它繼承了PersonService類。
public class PersonService$$EnhancerByCGLIB$$eaaaed75
extends PersonService
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$setPerson$0$Method;//被代理方法
private static final MethodProxy CGLIB$setPerson$0$Proxy;//代理方法
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1()
{
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class localClass1 = Class.forName("com.demo.proxy.cglib.PersonService$$EnhancerByCGLIB$$eaaaed75");//代理類
Class localClass2;//被代理類PersionService
Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = tmp95_92[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
Method[] tmp115_95 = tmp95_92;
CGLIB$equals$2$Method = tmp115_95[1];
CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
Method[] tmp135_115 = tmp115_95;
CGLIB$toString$3$Method = tmp135_115[2];
CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
Method[] tmp155_135 = tmp135_115;
CGLIB$hashCode$4$Method = tmp155_135[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
Method[] tmp175_155 = tmp155_135;
CGLIB$clone$5$Method = tmp175_155[4];
CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
tmp175_155;
Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.demo.proxy.cglib.PersonService")).getDeclaredMethods());
CGLIB$setPerson$0$Method = tmp223_220[0];
CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0");
tmp223_220;
return;
}我們通過代理類的源碼可以看到,代理類會(huì)獲得所有在父類繼承來的方法,并且會(huì)有MethodProxy與之對(duì)應(yīng),比如 Method CGLIB$setPerson 0 0 0Method、MethodProxy CGLIB$setPerson 0 0 0Proxy;
方法的調(diào)用
//代理方法(methodProxy.invokeSuper會(huì)調(diào)用)
final void CGLIB$setPerson$0() {
super.setPerson();
}
//被代理方法(methodProxy.invoke會(huì)調(diào)用,這就是為什么在攔截器中調(diào)用methodProxy.invoke會(huì)死循環(huán),一直在調(diào)用攔截器)
public final void setPerson() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) { //調(diào)用攔截器
var10000.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy);
} else {
super.setPerson();
}
}
調(diào)用過程:代理對(duì)象調(diào)用this.setPerson方法->調(diào)用攔截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理對(duì)象setPerson方法
MethodProxy
攔截器MethodInterceptor中就是由MethodProxy的invokeSuper方法調(diào)用代理方法的,MethodProxy非常關(guān)鍵,我們分析一下它具體做了什么。
創(chuàng)建MethodProxy
package com.zzhua.cglib.callback;
import net.sf.cglib.core.AbstractClassGenerator;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.NamingPolicy;
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
private final Object initLock = new Object();
private volatile MethodProxy.FastClassInfo fastClassInfo;
//c1:被代理對(duì)象Class
//c2:代理對(duì)象Class
//desc:入?yún)㈩愋?
//name1:被代理方法名
//name2:代理方法名
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);//被代理方法簽名
proxy.sig2 = new Signature(name2, desc);//代理方法簽名
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
return proxy;
}
private static class CreateInfo {
Class c1;
Class c2;
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;
public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
this.namingPolicy = fromEnhancer.getNamingPolicy();
this.strategy = fromEnhancer.getStrategy();
this.attemptLoad = fromEnhancer.getAttemptLoad();
}
}
}
}
invokeSuper調(diào)用
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
private static class FastClassInfo {
FastClass f1;//被代理類FastClass
FastClass f2;//代理類FastClass
int i1; //被代理類的方法簽名(index)
int i2;//代理類的方法簽名
private FastClassInfo() {
}
}
上面代碼調(diào)用過程就是獲取到代理類對(duì)應(yīng)的FastClass,并執(zhí)行了代理方法。還記得之前生成三個(gè)class文件嗎?
PersonService$$EnhancerByCGLIB$$eaaaed75$$FastClassByCGLIB$$355cb7ea.class就是代理類的FastClass,
PersonService$$FastClassByCGLIB$$a076b035.class就是被代理類的FastClass。
FastClass機(jī)制
Cglib動(dòng)態(tài)代理執(zhí)行代理方法效率之所以比JDK的高是因?yàn)镃glib采用了FastClass機(jī)制,它的原理簡單來說就是:為代理類和被代理類各生成一個(gè)Class,這個(gè)Class會(huì)為代理類或被代理類的方法分配一個(gè)index(int類型)。
這個(gè)index當(dāng)做一個(gè)入?yún)?,F(xiàn)astClass就可以直接定位要調(diào)用的方法直接進(jìn)行調(diào)用,這樣省去了反射調(diào)用,所以調(diào)用效率比JDK動(dòng)態(tài)代理通過反射調(diào)用高。
下面我們反編譯一個(gè)FastClass看看:
//根據(jù)方法簽名獲取index
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -2077043409:
if(var10000.equals("getPerson(Ljava/lang/String;)Lcom/demo/pojo/Person;")) {
return 21;
}
break;
case -2055565910:
if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 12;
}
break;
case -1902447170:
if(var10000.equals("setPerson()V")) {
return 7;
}
break;
//省略部分代碼.....
//根據(jù)index直接定位執(zhí)行方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
eaaaed75 var10000 = (eaaaed75)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
return new Boolean(var10000.equals(var3[0]));
case 1:
return var10000.toString();
case 2:
return new Integer(var10000.hashCode());
case 3:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 4:
return var10000.newInstance((Callback)var3[0]);
case 5:
return var10000.newInstance((Callback[])var3[0]);
case 6:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 7:
var10000.setPerson();
return null;
case 8:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 9:
return var10000.getCallback(((Number)var3[0]).intValue());
case 10:
return var10000.getCallbacks();
case 11:
eaaaed75.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 12:
eaaaed75.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 13:
return eaaaed75.CGLIB$findMethodProxy((Signature)var3[0]);
case 14:
return var10000.CGLIB$toString$3();
case 15:
return new Boolean(var10000.CGLIB$equals$2(var3[0]));
case 16:
return var10000.CGLIB$clone$5();
case 17:
return new Integer(var10000.CGLIB$hashCode$4());
case 18:
var10000.CGLIB$finalize$1();
return null;
case 19:
var10000.CGLIB$setPerson$0();
return null;
//省略部分代碼....
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
FastClass并不是跟代理類一塊生成的,而是在第一次執(zhí)行MethodProxy invoke/invokeSuper時(shí)生成的并放在了緩存中。
//MethodProxy invoke/invokeSuper都調(diào)用了init()
private void init() {
if(this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if(this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
fci.f1 = helper(ci, ci.c1);//如果緩存中就取出,沒有就生成新的FastClass
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);//獲取方法的index
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
至此,Cglib動(dòng)態(tài)代理的原理我們就基本搞清楚了,代碼細(xì)節(jié)有興趣可以再研究下。
最后我們總結(jié)一下JDK動(dòng)態(tài)代理和Gglib動(dòng)態(tài)代理的區(qū)別:
1.JDK動(dòng)態(tài)代理是實(shí)現(xiàn)了被代理對(duì)象的接口,Cglib是繼承了被代理對(duì)象。
2.JDK和Cglib都是在運(yùn)行期生成字節(jié)碼,JDK是直接寫Class字節(jié)碼,Cglib使用ASM框架寫Class字節(jié)碼,Cglib代理實(shí)現(xiàn)更復(fù)雜,生成代理類比JDK效率低。
3.JDK調(diào)用代理方法,是通過反射機(jī)制調(diào)用,Cglib是通過FastClass機(jī)制直接調(diào)用方法,Cglib執(zhí)行效率更高。
到此這篇關(guān)于Java的Cglib動(dòng)態(tài)代理實(shí)現(xiàn)方式詳解的文章就介紹到這了,更多相關(guān)Cglib動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis_plus基礎(chǔ)教程(總結(jié)篇)
這篇文章主要介紹了Mybatis_plus基礎(chǔ)教程(總結(jié)篇),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Eclipse項(xiàng)目有紅感嘆號(hào)的解決方法
這篇文章主要為大家詳細(xì)介紹了Eclipse項(xiàng)目有紅感嘆號(hào)的解決方法,給出了Eclipse項(xiàng)目有紅感嘆號(hào)的原因,以及如何解決?,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Spring項(xiàng)目運(yùn)行依賴spring-contex解析
這篇文章主要介紹了Spring項(xiàng)目運(yùn)行依賴spring-contex解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
java中List刪除時(shí)需要的注意事項(xiàng)
最近在利用java中的LIST在刪除時(shí)發(fā)現(xiàn)了一個(gè)錯(cuò)我,通過查找相關(guān)的資料終于解決了,覺著有必要分享處理給同樣遇到這個(gè)問題的朋友參考,下面這篇文章主要介紹了java中List刪除時(shí)需要的注意事項(xiàng),需要的朋友可以一起來看看吧。2017-01-01
java遞歸實(shí)現(xiàn)拼裝多個(gè)api的結(jié)果操作方法
本文給大家分享java遞歸實(shí)現(xiàn)拼裝多個(gè)api的結(jié)果的方法,說白了就是好幾個(gè)API結(jié)果拼裝成的,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-09-09
MyBatis環(huán)境資源配置實(shí)現(xiàn)代碼詳解
這篇文章主要介紹了MyBatis環(huán)境資源配置實(shí)現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
java中靜態(tài)代碼塊與構(gòu)造方法的執(zhí)行順序判斷
對(duì)靜態(tài)代碼塊以及構(gòu)造函數(shù)的執(zhí)行先后順序,一直很迷惑,直到最近看到一段代碼,發(fā)現(xiàn)終于弄懂了,所以這篇文章主要給大家介紹了關(guān)于如何判斷java中靜態(tài)代碼塊與構(gòu)造方法的執(zhí)行順序的相關(guān)資料,需要的朋友可以參考下。2017-12-12
Java實(shí)現(xiàn)拖拽文件上傳dropzone.js的簡單使用示例代碼
本篇文章主要介紹了Java實(shí)現(xiàn)拖拽文件上傳dropzone.js的簡單使用示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07

