在看這篇文章前,推薦先看一下個人jdk的動態代理。重複的東西在這兒我就不重複說了。java
public class MyService {
public void print(){
System.out.println("this is print!");
}
}
複製代碼
public class CglibIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("this is before!");
Object result = proxy.invokeSuper(obj, args);
System.out.println("this is after!");
return result;
}
}
複製代碼
public class CglibDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class);
enhancer.setCallback(new CglibIntercepter());
MyService service = (MyService) enhancer.create();
service.print();
}
}
複製代碼
不出意外的話,控制檯會打印bash
this is before!
this is print!
this is after!
複製代碼
這說明咱們的方法獲得了加強。ide
若是已經看過jdk的動態代理這篇文章的話,那你對cglib動態代理應該也是有一點想法的。他們實現的手段類似,都是經過字節碼技術來生成新類。post
因此新類生成的地方我就不帶着找了。根據方法參數和返回值很容易分析到。直接來看一下cglib生成的新類吧。測試
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"d:/cglib");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class);
enhancer.setCallback(new CglibIntercepter());
MyService service = (MyService) enhancer.create();
service.print();
複製代碼
在測試代碼最上面增長一行,設置cglib將新類的class文件打印的地方。設置好了運行一遍,不出意外能夠在d盤的cglib文件夾中找到D:\cglib*\*\cglib_proxy這個文件夾,裏面有三個類。一個繼承自MyService,兩個繼承自fastclass, 首先來看一下繼承自MyService的這個類。ui
不用猜也知道,這個就是cglib加強後的類了。因此找關鍵代碼。this
public final void print() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$print$0$Method, CGLIB$emptyArgs, CGLIB$print$0$Proxy);
} else {
super.print();
}
}
複製代碼
當咱們調用了print方法後,實際上是調用的var10000.intercept()方法,var10000你可能有點陌生,MethodInterceptor可是這個應該認識吧。這個就是咱們用來建立攔截器的接口。而這邊的var10000,其實就是咱們建立的那個攔截器。spa
因此攔截器如何加強方法到這邊應該清楚了,下面就跟蹤一下MethodProxy的invokeSuper方法。3d
首先經過靜態代碼塊得知MethodProxy的來源代理
CGLIB$print$0$Proxy = MethodProxy.create(var1, var0, "()V", "print", "CGLIB$print$0");
複製代碼
var1表明着原來的類,var0表明新類,跟蹤進入
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 CreateInfo(c1, c2);
return proxy;
}
複製代碼
只是作了些標記,搞清楚標記的順序後,開始分析invokeSuper方法。
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
複製代碼
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
複製代碼
init方法關鍵點,f1和f2分別是啥?i1 和i2又分別是啥?進入helper方法
FastClass.Generator g = new FastClass.Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
複製代碼
FastClass.Generator和咱們用來加強類的Enhancer類同樣,都繼承自AbstractClassGenerator類,而這邊調用了create方法,顯然也是生成了一個新類,那這個新類在哪裏呢?
還記得一開始在文件夾中的另外兩個類麼,其實他們就是這邊生成的fastclass類。打開那兩個類,進入getIndex方法
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1166389848:
if (var10000.equals("print()V")) {
return 0;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 1;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return 2;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 3;
}
}
return -1;
}
複製代碼
邏輯很簡單,就算根據方法的hash嗎,返回int標誌。因此到這兒f1,f2,fci.i1,fci.i2就都應該知道是啥含義了。
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
MyService var10000 = (MyService)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.print();
return null;
case 1:
return new Boolean(var10000.equals(var3[0]));
case 2:
return var10000.toString();
case 3:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
複製代碼
其實我以爲到這兒已經沒啥好說的了,先在init方法中,得到每一個方法的標誌,在拿着標誌去執行invoke方法。不一樣的標誌執行不一樣的方法,就這樣。
須要注意的是,jdk動態代理,執行方法是經過反射執行的,而cglib動態代理,是經過標誌區分,直接執行原類的方法,因此效率更高。