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();
}
}
}
到此,结束了。
关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。