MethodProxy調用分析
代碼的開始,還是從第一篇的那個例子開始,代碼我就不貼了。直接看MethodProxy
他是在MethodInterceptor裡面的入參,通過它可以調用原始的方法,也可以調用父類的方法,也可以調用同一個類型的不同對象。
重點就是最後一個,他是可以調用同一個類型的不同對象的。
方法分析
create(Class, Class, String, String, String)
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;
}
它是一個靜态的方法,給Enhancer使用的,通過它可以建立MethodProxy。對應代理類裡面的代碼如下圖所示
(這個代理類就是一開始測試的例子)。
需要注意的是它的幾個參數:對比上面的圖檔來看
- Class c1:被代理類的class對象
- Class c2:代理類的class對象
- String desc:方法的描述資訊(方法的參數,異常)
- String name1:被代理類的同一個描述資訊的方法的名字。
- String name2:代理類的同一個描述資訊的方法的名字。
對比這些入參,看一下MethodProxy的屬性的含義:
屬性解釋
private Signature sig1;
private Signature sig2;
private CreateInfo createInfo;
private final Object initLock = new Object();
private volatile FastClassInfo fastClassInfo;
- Signature sig1: 被代理類的方法簽名
- Signature sig2: 代理類的方法簽名
- CreateInfo createInfo: 它是一個建立代理對象的上下文對象,c1表示被代理的類,c2表示代理類,剩下的三個就是從目前建立代理對象的上下文中擷取屬性值而已。
- FastClassInfo fastClassInfo: 會之後再init方法裡面會建立。它就是之後通過
和Signature
建立出來的類的引用。他有兩個屬性,一個是代理類,一個被代理類的。CreateInfo
- Object initLock: 鎖
invokeSuper
通過MethodProxy調用父類方法。
Object obj: 代理類
Object[]:方法的入參。
再init方法裡面會建立兩個FastClass的子類(下面會分析),之後,會調用基于代理類生成的FastClass的invoke方法,這裡面會做真正的方法調用。
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();
}
}
在這裡面會調用init方法,來做初始化。先看init方法
init方法
通過CreateInfo和Signature建立FastClassInfo對象。
在建立FastClassInfo的時候,會動态的生成兩個類。他們會繼承FastClass對象,并且指派為FastClassInfo的f1和f2屬性,然後再生成的類裡面通過方法的簽名找下标,指派為FastClassInfo的i1和i2屬性。這個下标對應的生成的FastClass裡面getIndex方法傳回的值。
也就是FastClassInfo的f1和f2的屬性為代理類和被代理類生成的FastClass對象,i1和i2儲存的是這次調用方法再各自的FastClass對象裡面的下标。
這個下标是生成的類的
getIndex
方法傳回的下标,至于這個方法長什麼樣子,繼續往下看。
private void init()
{
if (fastClassInfo == null)
{
synchronized (initLock)
{ // double check
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
// 建立FastClass類,被代理的類
fci.f1 = helper(ci, ci.c1);
// 代理的類
fci.f2 = helper(ci, ci.c2);
// 直接調用方法,調用的是f1的getIndex方法,方法的簽名是被代理類再此次調用裡面的方法的簽名。
fci.i1 = fci.f1.getIndex(sig1);
// 同樣
fci.i2 = fci.f2.getIndex(sig2);
// 指派
fastClassInfo = fci;
// 變為null的目的是為了防止再次初始化。直接把報錯都比悄無聲息的出錯好。
createInfo = null;
}
}
}
}
重點就是
helper
方法了,這方法裡面會通過Cglib來生成類,它肯定有一個内部類,然後繼承于
AbstractClassGenerator
,實作
generateClass
方法,然後自己通過
ClassEmitter
的子類來編寫這個類。
Help方法分析
private static FastClass helper(CreateInfo ci, Class type) {
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);
return g.create();
}
顯然是繼承了
AbstractClassGenerator
,關于它裡面是怎麼做的,我就不分析了。因為我也不太懂。最後直接看生成的FastClass長什麼樣子,可以看一點東西,比如圖Soruce,Key,🤣。
invoke方法
和上面的
invokeSuper
一樣,一上來先是init,但是它調用的是FastClassInfo這個i不過的f1的invoke方法。(也就是被代理類生成的FastClass)
通過上面的分析,知道FastClassInfo的含義了,FastClassInfo裡面有四個屬性,在MethodProxy裡面會建立兩個動态的類,這兩個類都是繼承于FastClass的,FastClassInfo中的f1和f2屬性對應的是被代理的類和代理類對應生成的FastClass的引用。i1和i2對應的是此次調用的方法在生成的FastClass類中getIndex方法傳回的值。
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (IllegalArgumentException e) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw e;
}
}
除了上面說的這兩個方法之外,MethodProxy還有幾個方法,下面我也列舉出說明一下。
- getSignature:傳回被代理對象的被代理方法的簽名
- getSuperName:方法代理對象(通過cglib生成的)沒有攔截器的直接調用super的方法的簽名
- getSuperIndex:傳回的是為代理對象生成的FastClass中,通過getIndex傳回的下标。
- find:傳回給定類型的方法簽名。
這些方法在日常的使用的機會是比較少的。重點還是上面說的方法
說到這裡,就剩下看看生成的FastClass長什麼樣子了?
生成的FastClass長什麼樣子?
- 為被代理類生成的FastClass
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package net.sf.cglib.samples.simple;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
public class Bean$$FastClassByCGLIB$$62952930 extends FastClass {
public Bean$$FastClassByCGLIB$$62952930(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1816210712:
if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;")) {
return 5;
}
break;
case -1123095605:
if (var10000.equals("hhhh()Ljava/lang/String;")) {
return 3;
}
break;
case -908043773:
if (var10000.equals("sasasasa()Ljava/lang/String;")) {
return 2;
}
break;
case -286557062:
if (var10000.equals("lipu1()Ljava/lang/String;")) {
return 0;
}
break;
case 580486146:
if (var10000.equals("dadada()Ljava/lang/String;")) {
return 6;
}
break;
case 1631032358:
if (var10000.equals("lululu()Ljava/lang/String;")) {
return 1;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 7;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return 8;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 9;
}
break;
case 2130387705:
if (var10000.equals("jump()Ljava/lang/String;")) {
return 4;
}
}
return -1;
}
public int getIndex(String var1, Class[] var2) {
switch(var1.hashCode()) {
case -2012993625:
if (var1.equals("sayHello")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.String")) {
return 5;
}
}
}
break;
case -1776922004:
if (var1.equals("toString")) {
switch(var2.length) {
case 0:
return 8;
}
}
break;
case -1339395145:
if (var1.equals("dadada")) {
switch(var2.length) {
case 0:
return 6;
}
}
break;
case -1295482945:
if (var1.equals("equals")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.Object")) {
return 7;
}
}
}
break;
case -1091633701:
if (var1.equals("lululu")) {
switch(var2.length) {
case 0:
return 1;
}
}
break;
case 3201536:
if (var1.equals("hhhh")) {
switch(var2.length) {
case 0:
return 3;
}
}
break;
case 3273774:
if (var1.equals("jump")) {
switch(var2.length) {
case 0:
return 4;
}
}
break;
case 102979631:
if (var1.equals("lipu1")) {
switch(var2.length) {
case 0:
return 0;
}
}
break;
case 147696667:
if (var1.equals("hashCode")) {
switch(var2.length) {
case 0:
return 9;
}
}
break;
case 2133693496:
if (var1.equals("sasasasa")) {
switch(var2.length) {
case 0:
return 2;
}
}
}
return -1;
}
public int getIndex(Class[] var1) {
switch(var1.length) {
case 0:
return 0;
default:
return -1;
}
}
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
Bean var10000 = (Bean)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
return var10000.lipu1();
case 1:
return var10000.lululu();
case 2:
return var10000.sasasasa();
case 3:
return var10000.hhhh();
case 4:
return var10000.jump();
case 5:
return var10000.sayHello((String)var3[0]);
case 6:
return var10000.dadada();
case 7:
return new Boolean(var10000.equals(var3[0]));
case 8:
return var10000.toString();
case 9:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
Bean var10000 = new Bean;
Bean var10001 = var10000;
int var10002 = var1;
try {
switch(var10002) {
case 0:
var10001.<init>();
return var10000;
}
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public int getMaxIndex() {
return 9;
}
}
- 為代理類生成的FastClass
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package net.sf.cglib.samples.simple;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.samples.simple.Bean..EnhancerByCGLIB..d79f67a6;
public class Bean$$EnhancerByCGLIB$$d79f67a6$$FastClassByCGLIB$$cf2d2fec extends FastClass {
public Bean$$EnhancerByCGLIB$$d79f67a6$$FastClassByCGLIB$$cf2d2fec(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -2144987997:
if (var10000.equals("CGLIB$lipu1$0()Ljava/lang/String;")) {
return 18;
}
break;
case -2055565910:
if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 11;
}
break;
case -1816210712:
if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;")) {
return 16;
}
break;
case -1457535688:
if (var10000.equals("CGLIB$STATICHOOK1()V")) {
return 20;
}
break;
case -1123095605:
if (var10000.equals("hhhh()Ljava/lang/String;")) {
return 14;
}
break;
case -908043773:
if (var10000.equals("sasasasa()Ljava/lang/String;")) {
return 13;
}
break;
case -894172689:
if (var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
return 3;
}
break;
case -623122092:
if (var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;")) {
return 19;
}
break;
case -419626537:
if (var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V")) {
return 6;
}
break;
case -286557062:
if (var10000.equals("lipu1()Ljava/lang/String;")) {
return 7;
}
break;
case 560567118:
if (var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V")) {
return 8;
}
break;
case 580486146:
if (var10000.equals("dadada()Ljava/lang/String;")) {
return 17;
}
break;
case 811063227:
if (var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
return 5;
}
break;
case 973717575:
if (var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;")) {
return 10;
}
break;
case 1221173700:
if (var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
return 4;
}
break;
case 1230699260:
if (var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;")) {
return 9;
}
break;
case 1584330438:
if (var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 12;
}
break;
case 1631032358:
if (var10000.equals("lululu()Ljava/lang/String;")) {
return 21;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 0;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return 1;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 2;
}
break;
case 2130387705:
if (var10000.equals("jump()Ljava/lang/String;")) {
return 15;
}
}
return -1;
}
public int getIndex(String var1, Class[] var2) {
switch(var1.hashCode()) {
case -2012993625:
if (var1.equals("sayHello")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.String")) {
return 16;
}
}
}
break;
case -1776922004:
if (var1.equals("toString")) {
switch(var2.length) {
case 0:
return 1;
}
}
break;
case -1339395145:
if (var1.equals("dadada")) {
switch(var2.length) {
case 0:
return 17;
}
}
break;
case -1295482945:
if (var1.equals("equals")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.Object")) {
return 0;
}
}
}
break;
case -1091633701:
if (var1.equals("lululu")) {
switch(var2.length) {
case 0:
return 21;
}
}
break;
case -1053468136:
if (var1.equals("getCallbacks")) {
switch(var2.length) {
case 0:
return 10;
}
}
break;
case -60403779:
if (var1.equals("CGLIB$SET_STATIC_CALLBACKS")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
return 12;
}
}
}
break;
case 3201536:
if (var1.equals("hhhh")) {
switch(var2.length) {
case 0:
return 14;
}
}
break;
case 3273774:
if (var1.equals("jump")) {
switch(var2.length) {
case 0:
return 15;
}
}
break;
case 85179481:
if (var1.equals("CGLIB$SET_THREAD_CALLBACKS")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
return 11;
}
}
}
break;
case 102979631:
if (var1.equals("lipu1")) {
switch(var2.length) {
case 0:
return 7;
}
}
break;
case 147696667:
if (var1.equals("hashCode")) {
switch(var2.length) {
case 0:
return 2;
}
}
break;
case 161998109:
if (var1.equals("CGLIB$STATICHOOK1")) {
switch(var2.length) {
case 0:
return 20;
}
}
break;
case 495524492:
if (var1.equals("setCallbacks")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
return 6;
}
}
}
break;
case 1154623345:
if (var1.equals("CGLIB$findMethodProxy")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("net.sf.cglib.core.Signature")) {
return 19;
}
}
}
break;
case 1264770776:
if (var1.equals("CGLIB$lipu1$0")) {
switch(var2.length) {
case 0:
return 18;
}
}
break;
case 1811874389:
if (var1.equals("newInstance")) {
switch(var2.length) {
case 1:
String var10001 = var2[0].getName();
switch(var10001.hashCode()) {
case -845341380:
if (var10001.equals("net.sf.cglib.proxy.Callback")) {
return 3;
}
break;
case 1730110032:
if (var10001.equals("[Lnet.sf.cglib.proxy.Callback;")) {
return 4;
}
}
case 2:
default:
break;
case 3:
if (var2[0].getName().equals("[Ljava.lang.Class;") && var2[1].getName().equals("[Ljava.lang.Object;") && var2[2].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
return 5;
}
}
}
break;
case 1817099975:
if (var1.equals("setCallback")) {
switch(var2.length) {
case 2:
if (var2[0].getName().equals("int") && var2[1].getName().equals("net.sf.cglib.proxy.Callback")) {
return 8;
}
}
}
break;
case 1905679803:
if (var1.equals("getCallback")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("int")) {
return 9;
}
}
}
break;
case 2133693496:
if (var1.equals("sasasasa")) {
switch(var2.length) {
case 0:
return 13;
}
}
}
return -1;
}
public int getIndex(Class[] var1) {
switch(var1.length) {
case 0:
return 0;
default:
return -1;
}
}
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
d79f67a6 var10000 = (d79f67a6)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((Callback)var3[0]);
case 4:
return var10000.newInstance((Callback[])var3[0]);
case 5:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 6:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 7:
return var10000.lipu1();
case 8:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 9:
return var10000.getCallback(((Number)var3[0]).intValue());
case 10:
return var10000.getCallbacks();
case 11:
d79f67a6.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 12:
d79f67a6.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 13:
return var10000.sasasasa();
case 14:
return var10000.hhhh();
case 15:
return var10000.jump();
case 16:
return var10000.sayHello((String)var3[0]);
case 17:
return var10000.dadada();
case 18:
return var10000.CGLIB$lipu1$0();
case 19:
return d79f67a6.CGLIB$findMethodProxy((Signature)var3[0]);
case 20:
d79f67a6.CGLIB$STATICHOOK1();
return null;
case 21:
return var10000.lululu();
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
d79f67a6 var10000 = new d79f67a6;
d79f67a6 var10001 = var10000;
int var10002 = var1;
try {
switch(var10002) {
case 0:
var10001.<init>();
return var10000;
}
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public int getMaxIndex() {
return 21;
}
}
說明
FastClass本身是一個抽象類,生成的子類實作了他的抽象方法,主要有下面的幾個方法
-
getIndex:
在這個方法裡面會通過傳遞進來的要建立的類型,擷取他的所有方法,組成一個很大的switch,case。通過傳遞進來的方法的簽名的hash值,傳回對應的下标,這個下标和invoke裡面用的是一樣的。具體的可以看上面的
方法public int getIndex(Signature var1)
-
invoke
先看他的方法的入參:
int var1:表示傳遞過來的index,這個index就是getIndex傳回的。
Object var2:表示需要調用的對象。
Object[] var3: 方法的入參。
從代碼來看這裡很簡單,先強轉為調用的類型(如上面的(Bean)var2),之後通過入參(var,其實就是index)調用具體的方法。
代理類和被代理的類都是同樣的邏輯,隻不過強轉的時候類型不一緻,别的操作都是一緻的。
總結
在建構代理類的時候,如果攔截器的類型是MethodIntercept,就會建立針對MethodIntercept使用的方法的MethodProxy,在調用MethodIntercept的時候,會将MethodProxy傳遞過去。MethodProxy是在生成的代理類的靜态代碼塊裡面初始化的。(通過MethodProxy#create方法)。
建立MethodProxyd的時候會傳遞四個參數,分為對應,被代理的類對象的class對象,代理類對象的class對象,MethodIntercepted起作用的方法的簽名,這個方法對應在被代理類和代理類中的名字。通過這幾個參數給MethodProxy的幾個屬性指派,同時他們也有不同的語義。Cglib用Signature來表示方法的簽名(方法的名字和方法的描述資訊),對應MethodProxy的sig1(被代理類的方法簽名)和sig2(代理類的方法簽名)屬性。
此外,在MethodProxy還有一個FastClassInfo屬性,Cglib裡面在調用MethodProxy#invoke或者invokeSuper的時候,會針對sig1和sig2動态的生成兩個FastClass的子類,在這子類裡面會擷取要實作的類型的所有的方法,利用這些方法做調用,在生成的FastClass的子類裡面主要有兩個方法,getIndex和invoke方法。在getIndex裡面會利用方法的簽名傳回index,這些index對應的是該方法在invoke裡面的調用下标。
在invoke方法裡面将,傳遞進來的對象強轉為它擷取方法類型的class對象,通過下标,做方法調用。
簡單的說就是,MethodProxy在調用的時候,會動态的生成兩個繼承于FastClass類,它裡面有倆重要的方法,getindex和invoke方法,在類裡面會擷取到要實作類的class的所有的方法,這些方法在invoke裡面會組成從0開始的方法調用的下标。并且會将invoke方法傳遞進來的對象強轉為要實作的類的類型,通過下标做比對,做方法調用。在getIndex方法裡面,通過傳遞進來的方法簽名的hashcode,做比對,傳回index(這個index是對應的invoke方法裡面的index)。
可以看到,在invoke方法裡面,傳遞進去的主要是生成的FastClass要實作的類的子類,都是可以的。invoke裡面有強轉操作。
方法調用分析
上面分析了MethodProxy的屬性和FastClass的作用。這一章節,聚焦到方法的調用上。這裡說的方法調用是通過MethodIntercepted做調用的。通過前面的文章,前面的分析已經知道,剩餘的幾個callback的子接口是沒有調用父類的操作的。是沒有MethodProxy的。還是從一個例子開始。
重新來一個例子,看這個例子,就一個callback(MethodInterceptor),有TestBean和TestBean1,倆關系是泛化。并且子類重寫了父類的test1方法。在TestBean中是有方法嵌套調用的。
下面正對invokeSupe和invoke不同的調用方式做分析。
public class TestMain {
public static void main(String[] args) {
final TestBean testBean = new TestBean();
final TestBean1 testBean1 = new TestBean1();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestBean.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("intercept:" + method.getName());
return proxy.invokeSuper(obj,args);
}
});
TestBean o = (TestBean)enhancer.create();
System.out.println(o.test("param"));
}
}
class TestBean{
public String test(String param){
String s = test1(param);
return "test:" + s;
}
public String test1(String param){
return "test1:" + param;
}
}
class TestBean1 extends TestBean{
@Override
public String test1(String param) {
return "TestBean1:test1";
}
}
invokeSuper
結果
方法嵌套的調用被發現了。
分析:
想想上面生成的FastClass對象,在
invokeSuper
得時候,調用的是
MethodProxy#FastClassInfo#f2的invoke方法
MethodProxy在代理類裡面建立得時候傳遞的是。
從這可以看出來,getIndex傳遞的是
CGLIB$test1$1
,調用invokeSuper的時候,會調用到動态生成的FastClass裡面invoke方法,在invoke裡面會強轉為代理類,最終會調用到
CGLIB$test1$1
方法裡面去。是以,這這就解釋了invokeSuper裡面傳遞代理對象進去,因為代理對象才能轉換為代理對象。
最終會調用到
CGLIB$test1$1
(這個方法是Cglib自動生成的。隻要這個方法引用的MethodInterceptor,就會多一個MethodProxy和一個沒有methodInterceptor的方法,在這個方法裡面直接調用super方法。)
CGLIB$test1$1裡面是沒有調用MethodIntercepter方法的。因為她不能調用,如果調用,就會發生循環調用,一直在調用回調,導緻棧溢出。
調用到代理類的父類了,父類裡面再次調用了test1方法,test1方法也有代理,是以,又再次走到了上面的邏輯,再次invoke調用到父類的方法。
invokeSuper是可以檢測到方法之前的嵌套調用的。
invoke
- invoke方法傳參為代理對象
棧溢出,溢出的原因是:
因為在調用invoke的時候,他調用的為是被代理類的生成的fastClass裡面的invoke方法。
代理類肯定是能轉為TestBean,直接調用代理類的test方法,這和直接調用沒有什麼差别,還是會調用到代理類裡面,還是會調用到methodInterceptor裡面的。一直會循環下去。
- invoke方法傳參為普通的對象
調用正常,因為在為被代理類生成的fastclass的invoke方法裡面,會強轉為TestBean類型,傳遞進去的testBean就是TestBean。在invoke裡面直接強轉,然後直接調用。是以,沒有問題(Spring中切面的調用就是通過這種方式,是以才會導緻所謂的失效,這是本質原因)
-
invoke方法傳遞參數為被代理對象的子類對象
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-ZAcC3Vqn-
要注意,Testbean1是繼承TestBean的,是以在invoke方法裡面也是可以強轉為TestBean類型的,但是Testbean1重寫了test1方法。是以,在調用的時候就調用到了子類的方法。這種調用方式是符合FastClass#invoke的原本的意思的。
總結
MethodProxy調用 invokeSuper的時候 方法之間的調用是可以發現的。調用invoke的時候,方法之間的嵌套是不能發現的。
并且在,MethodProxy調用的時候,會針對代理類和被代理的類動态的建立兩個FastClass的子類,在invoke和invokeSuper的時候,會調用不同的FastClass類裡面的方法。
到這裡,關于Cglib的東西結束了,主要分析了Cglib的内部建立代理類的過程,和幾個有意思的屬性,這些屬性分别用于緩存,代理對象class名字的生成。還分析了Callback的各種子類,已經他們是在Cgliib中的使用,和Cglib怎麼來編寫這些方法的。此外。還分析MethodIntercepter中,MethodProxy調用的時相關的代碼邏輯。從根上分析了Cglib建立代理對象的時候,失效的問題(主要分為兩種,invokeSuper不會,invoke會)。
private void init()
{
/*
* Using a volatile invariant allows us to initialize the FastClass and
* method index pairs atomically.
*
* Double-checked locking is safe with volatile in Java 5. Before 1.5 this
* code could allow fastClassInfo to be instantiated more than once, which
* appears to be benign.
*/
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
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;
}
}
}
}
private static FastClass helper(CreateInfo ci, Class type) {
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);
return g.create();
}
private MethodProxy() {
}
/**
* Return the signature of the proxied method.
*/
public Signature getSignature() {
return sig1;
}
/**
* Return the name of the synthetic method created by CGLIB which is
* used by {@link #invokeSuper} to invoke the superclass
* (non-intercepted) method implementation. The parameter types are
* the same as the proxied method.
*/
public String getSuperName() {
return sig2.getName();
}
/**
* Return the {@link net.sf.cglib.reflect.FastClass} method index
* for the method used by {@link #invokeSuper}. This index uniquely
* identifies the method within the generated proxy, and therefore
* can be useful to reference external metadata.
* @see #getSuperName
*/
public int getSuperIndex() {
init();
return fastClassInfo.i2;
}
// For testing
FastClass getFastClass() {
init();
return fastClassInfo.f1;
}
// For testing
FastClass getSuperFastClass() {
init();
return fastClassInfo.f2;
}
/**
* Return the <code>MethodProxy</code> used when intercepting the method
* matching the given signature.
* @param type the class generated by Enhancer
* @param sig the signature to match
* @return the MethodProxy instance, or null if no applicable matching method is found
* @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
*/
public static MethodProxy find(Class type, Signature sig) {
try {
Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
MethodInterceptorGenerator.FIND_PROXY_TYPES);
return (MethodProxy)m.invoke(null, new Object[]{ sig });
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
} catch (IllegalAccessException e) {
throw new CodeGenerationException(e);
} catch (InvocationTargetException e) {
throw new CodeGenerationException(e);
}
}
/**
* Invoke the original method, on a different object of the same type.
* @param obj the compatible object; recursion will result if you use the object passed as the first
* argument to the MethodInterceptor (usually not what you want)
* @param args the arguments passed to the intercepted method; you may substitute a different
* argument array as long as the types are compatible
* @see MethodInterceptor#intercept
* @throws Throwable the bare exceptions thrown by the called method are passed through
* without wrapping in an <code>InvocationTargetException</code>
*/
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (IllegalArgumentException e) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw e;
}
}
/**
* Invoke the original (super) method on the specified object.
* @param obj the enhanced object, must be the object passed as the first
* argument to the MethodInterceptor
* @param args the arguments passed to the intercepted method; you may substitute a different
* argument array as long as the types are compatible
* @see MethodInterceptor#intercept
* @throws Throwable the bare exceptions thrown by the called method are passed through
* without wrapping in an <code>InvocationTargetException</code>
*/
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();
}
}
}
到此,結束了。
關于部落格這件事,我是把它當做我的筆記,裡面有很多的内容反映了我思考的過程,因為思維有限,不免有些内容有出入,如果有問題,歡迎指出。一同探讨。謝謝。