本文的写作目的是为了探究 Spring 框架中在使用@Transactional标注的方法中使用 this 进行自调用时事务失效的原因,各种视频教程中只是简单指出 this 指向的不是代理类对象,而是目标类对象,但是并没有解释为什么 this 不是代理类对象?
在学习完 JDK 动态代理之后,我认为是动态代理的原因。虽然知道 Cglib Proxy 和 JDK Proxy 的实现原理不同,但当时认为方法调用只能通过 invoke 进行反射调用(错误依据),而传递给 invoke 方法的对象就是目标类对象,因此 this 指向的就是传递过来的目标类对象。具体可以查看另一篇博客。
最近学习完 Cglib 动态代理之后,发现动态代理类进行方法调用并不是只能依靠反射调用的,因此那一篇博客的分析也就不成立了。先说结论,在 Cglib 动态代理中,由于绕开了反射调用方法,所以 this 既可以指向代理类对象,也可以和 JDK 动态代理一样指向目标类对象,而 Spring 框架中选择了后者,从而有了 this 造成事务失效的情况。但是就 Cglib 本身实现动态代理而言,这个问题是可以避免的。
下面两个 pom 依赖二选一即可
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.3.30version>
dependency>
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.1version>
dependency>
public class CglibProxyTest {
private static final String CLASSPATH = ClassLoader.getSystemResource("").getPath().substring(1);
public static void main(String[] args) {
// 设置生成字节码文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, CLASSPATH);
MethodInterceptor methodInterceptor = new MyMethodInterceptor();
Enhancer enhancer = new Enhancer();
// 设置父类字节码
enhancer.setSuperclass(ServiceImpl.class);
// 设置增强方法(即方法拦截器)
// TODO: 从代理类的源码中可以看出来,setCallbacks只会使用到第一个拦截器,那么setCallbacks方法有什么意义呢?
enhancer.setCallback(methodInterceptor);
ServiceImpl serviceImpl = (ServiceImpl) enhancer.create();
serviceImpl.show("Hello World");
// 测试cglib代理方式下的this自调用和Spring事务的this自调用的区别
//serviceImpl.getMsg(1, 2);
}
}
class MyMethodInterceptor implements MethodInterceptor {
/**
* 拦截方法
*
* @param proxy 代理类对象
* @param targetMethod 目标类中的方法对象
* @param args 方法参数
* @param methodProxy Cglib底层使用到的MethodProxy对象,并不是代理方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method targetMethod, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("proxy.getClass() = " + proxy.getClass());
System.out.println("targetMethod.getName() = " + targetMethod.getName());
System.out.println("targetMethod.getDeclaringClass().getName() = " + targetMethod.getDeclaringClass().getName());
System.out.println("===========targetMethod before==========");
// 可能出现的情况
// 情况一:methodProxy.invoke(proxy, args) 陷入invoke -> intercept -> invoke ->...的无限递归中
//
// 情况二:targetMethod.invoke(proxy, args) 陷入 intercept->show->intercept的死循环中。
// 这是因为targetMethod是一个Method方法对象,只有invoke方法,此时没有涉及到MethodProxy的invoke和invokeSuper方法
// 而proxy是targetClass的一个子类对象,因此targetMethod.invoke(proxy, args)相当于在调用proxy对象中的同名targetMethod方法,即增强方法。所以陷入死循环
// Spring使用this无法增强的原因,使用下面的情况三方案,其中target对象是外部传入的,和MethodInvocationHandler一样
// 情况三:methodProxy.invoke(target, args)
//
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println("===========targetMethod after===========");
return result;
}
}
/**
* 目标类(被代理类)不存在接口
*/
class ServiceImpl {
public void show(String msg) {
System.out.println(msg);
}
public String getMsg(int x, int y) {
this.show("Hello World");
return String.valueOf(x + y);
}
}
对反编译后的源代码文件进行变量名的调整,删减一些非核心的细节内容。
从重写的增强方法中可以看出来,传递给 intercept()
方法的参数的含义分别是:
Object
:(ServiceImplCglibProxy)代理类对象(this)Method
:代理类中对于目标类方法的对象引用,即上图中的 Method01
、Method02
、Method03
Object[]
:方法参数数组(args)MethodProxy
:根据代理类中的 cglibMethod0x()
和 method0x()
生成的 MethodProxy 对象public class ServiceImplCglibProxy
extends ServiceImpl
implements Factory {
// 目标类中的的方法对象(show)
private static Method showMethod;
// 代理类中show方法和cglibShow方法共同构建的MethodProxy对象
private static MethodProxy showMethodProxy;
// 目标类中的的方法对象(show)
private static Method getMsgMethod;
// 代理类中getMsg方法和cglibGetMsg方法共同构建的MethodProxy对象
private static MethodProxy getMsgMethodProxy;
// 用来包装Object[0]
private static final Object[] emptyArgs = new Object[0];
// Object类中的方法
private static Method finalizeMethod;
private static MethodProxy finalizeMethodProxy;
// 判断是否绑定过ThreadLocal中的Callback,如果绑定过,那么之后就不需要绑定了
private boolean bound;
// 自定义增强逻辑,MethodInterceptor是Callback的一个子接口,在实例化的时候会通过调用静态方法进行设置,省略
private MethodInterceptor methodInterceptor;
static {
init();
}
@SneakyThrows
static void init() {
// 获取当前代理类的字节码对象
Class proxyClass = Class.forName("org.example.ServiceImplCglibProxy");
Method[] methods;
// 处理Object类中的所有方法
Class targetClass = Class.forName("java.lang.Object");
methods = ReflectUtils.findMethods(
new String[]{
"finalize", "()V",
"equals", "(Ljava/lang/Object;)Z",
"toString", "()Ljava/lang/String;",
"hashCode", "()I",
"clone", "()Ljava/lang/Object;"},
targetClass.getDeclaredMethods());
finalizeMethod = methods[0];
finalizeMethodProxy = MethodProxy.create(targetClass, proxyClass, "()V", "finalize", "cglibFinalize");
// 省略 Object 类中的其他方法的处理...
// 处理ServiceImpl(父类)中的所有方法
targetClass = Class.forName("org.example.ServiceImpl");
methods = ReflectUtils.findMethods(
new String[]{
"show", "(Ljava/lang/String;)V",
"getMsg", "(II)Ljava/lang/String;"
},
targetClass.getDeclaredMethods());
// 处理ServiceImpl中的show方法
showMethod = methods[0];
showMethodProxy = MethodProxy.create(targetClass, proxyClass, "(Ljava/lang/String;)V", "show", "cglibShow");
// 处理ServiceImpl中的getMsg方法
getMsgMethod = methods[1];
getMsgMethodProxy = MethodProxy.create(targetClass, proxyClass, "(II)Ljava/lang/String;", "getMsg", "cglibGetMsg");
}
public static MethodProxy findMethodProxy(Signature signature) {
String methodSignature = signature.toString();
// 先比较hashCode值,再比较字符串
switch (methodSignature.hashCode()) {
case -1574182249:
if (methodSignature.equals("finalize()V")) {
return finalizeMethodProxy;
}
break;
case 550733602:
if (methodSignature.equals("show(Ljava/lang/String;)V")) {
return showMethodProxy;
}
break;
case 351083702:
if (methodSignature.equals("getMsg(II)Ljava/lang/String;")) {
return getMsgMethodProxy;
}
break;
}
return null;
}
// 为便于区分,称为cglib方法
void cglibShow(String msg) {
super.show(msg);
}
// 为便于区分,称为重写方法
@SneakyThrows
@Override
public void show(String msg) {
if(methodInterceptor == null){
// 当使用methodProxy.invoke(target, args),由于target没有methodInterceptor,所有会进入到这个逻辑分支中
super.show(msg);
return;
}
// 将参数封装成Object数组,调用intercept方法
// 在正常传递MethodInterceptor的情况下,会调用该方法
methodInterceptor.intercept(this, showMethod, new Object[]{msg}, showMethodProxy);
}
// 为便于区分,称为cglib方法
String cglibGetMsg(int x, int y) {
return super.getMsg(x, y);
}
// 为便于区分,称为重写方法
@SneakyThrows
@Override
public String getMsg(int x, int y) {
if(methodInterceptor == null){
return super.getMsg(x, y);
}
return (String) methodInterceptor.intercept(this, getMsgMethod, new Object[]{x, y}, getMsgMethodProxy);
}
}
在进一步讨论增强方法的调用流程之前,先重新查看 MethodInterceptor 的 intercept()
方法。我们可以得到四个参数,那么怎么选择来达成我们调用原始方法的目的。
结论:
invokeSuper()
方法能够正确执行,同时 this 指针指向的是 ServiceImplCglibProxy(代理类)对象,因为不会出现像 Spring 中使用 @Transactional 标注的方法中使用 this 会造成事务失效的问题。invokeSuper()
方法外,另外两个方法都可以正确执行。class MyMethodInterceptor implements MethodInterceptor {
// 不是必要的,Spring注入target造成this无法调用自身对象,直接使用proxy对象就不会出现这种情况
private Object target;
@Override
public Object intercept(Object proxy, Method targetMethod, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("===========targetMethod before==========");
// 由于FastClass的机制,这里不能调用methodProxy.invoke(proxy, args),否则
// 正常可能出现的情况
// 情况一:methodProxy.invoke(proxy, args) 陷入invoke -> intercept -> invoke ->...的无限递归中
//
// 情况二:targetMethod.invoke(proxy, args) 陷入 intercept->show->intercept的死循环中。
// 这是因为targetMethod是一个Method方法对象,只有invoke方法,此时没有涉及到MethodProxy的invoke和invokeSuper方法
// 而proxy是targetClass的一个子类对象,因此targetMethod.invoke(proxy, args)相当于在调用proxy对象中的同名targetMethod方法,即增强方法。所以陷入死循环
// Spring使用this无法增强的原因,使用下面的情况三方案,其中target对象是外部传入的,和MethodInvocationHandler一样
// 情况三:methodProxy.invoke(target, args)
//
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println("===========targetMethod after===========");
return result;
}
}
主要关注 invoke()
方法和 invokeSuper()
方法,因为这两个方法会在 MethodInterceptor 对象的 intercept()
方法中被我们调用来达到调用目标类中的原始方法的目的。
public class MethodProxy {
private Signature overrideMethodSignature;
private Signature cglibMethodSignature;
// create方法中生成,init方法中清空
private CreateInfo createInfo;
private final Object initLock = new Object();
private volatile FastClassInfo fastClassInfo;
public static MethodProxy create(Class targetClass,
Class proxyClass,
String desc,
String overrideMethodName,
String cglibMethodName) {
MethodProxy proxy = new MethodProxy();
proxy.overrideMethodSignature = new Signature(overrideMethodName, desc);
proxy.cglibMethodSignature = new Signature(cglibMethodName, desc);
proxy.createInfo = new CreateInfo(targetClass, proxyClass);
return proxy;
}
public Object invoke(Object object, Object[] args) throws Throwable {
this.init();
FastClassInfo fci = this.fastClassInfo;
// 默认情况下 object 是 proxyClass 类型,那么 overrideMethod -> intercept -> invoke -> overrideMethod 形成死循环。
// 如果按照Spring的方式手动传递 targetClass 类型的对象,object是targetClass类型(目标类)
return fci.targetFastClass.invoke(fci.overrideMethodIndex, object, args);
}
public Object invokeSuper(Object proxy, Object[] args) throws Throwable {
this.init();
FastClassInfo fci = this.fastClassInfo;
// 重写父类方法时,留了一份拷贝,称之为cglibMethod,因此可以在当前类直接调用父类中的同名方法
return fci.proxyFastClass.invoke(fci.cglibMethodIndex, proxy, args);
}
private void init() {
if (this.fastClassInfo == null) {
synchronized (this.initLock) {
if (this.fastClassInfo == null) {
CreateInfo ci = this.createInfo;
FastClassInfo fci = new FastClassInfo();
fci.targetFastClass = helper(ci, ci.targetClass);
fci.proxyFastClass = helper(ci, ci.proxyClass);
// overrideMethodSignature在targetClass和proxyClass中的签名都相同
fci.overrideMethodIndex = fci.targetFastClass.getIndex(this.overrideMethodSignature);
fci.cglibMethodIndex = fci.proxyFastClass.getIndex(this.cglibMethodSignature);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
private static FastClass helper(CreateInfo ci, Class classType) {
FastClass.Generator generator = new FastClass.Generator();
generator.setType(classType);
generator.setClassLoader(ci.proxyClass.getClassLoader());
generator.setNamingPolicy(ci.namingPolicy);
generator.setStrategy(ci.strategy);
generator.setAttemptLoad(ci.attemptLoad);
return generator.create();
}
private MethodProxy() {
}
public Signature getSignature() {
return this.overrideMethodSignature;
}
public String getSuperName() {
return this.cglibMethodSignature.getName();
}
public int getSuperIndex() {
this.init();
return this.fastClassInfo.cglibMethodIndex;
}
FastClass getFastClass() {
this.init();
return this.fastClassInfo.targetFastClass;
}
FastClass getSuperFastClass() {
this.init();
return this.fastClassInfo.proxyFastClass;
}
@SneakyThrows
public static MethodProxy find(Class classType, Signature sig) {
Method method = classType.getDeclaredMethod("findMethodProxy", MethodInterceptorGenerator.FIND_PROXY_TYPES);
return (MethodProxy) method.invoke(null, sig);
}
private static class CreateInfo {
Class targetClass;
Class proxyClass;
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;
public CreateInfo(Class targetClass, Class proxyClass) {
this.targetClass = targetClass;
this.proxyClass = proxyClass;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
this.namingPolicy = fromEnhancer.getNamingPolicy();
this.strategy = fromEnhancer.getStrategy();
this.attemptLoad = fromEnhancer.getAttemptLoad();
}
}
}
private static class FastClassInfo {
FastClass targetFastClass;
FastClass proxyFastClass;
int overrideMethodIndex;
int cglibMethodIndex;
private FastClassInfo() {
}
}
}
为了避免使用 Method 的 invoke 方法来进行反射调用而设计的。
public Object invoke(int methodIndex, Object obj, Object[] args) throws InvocationTargetException {
// 这里无论传入的是ServiceImpl还是ServiceImplCglibProxy,都可以进行强转
// 如果是ServiceImplCglibProxy,就调用同名方法
ServiceImpl serviceImpl = (ServiceImpl)obj;
// 没有匹配就抛出异常
try {
switch (methodIndex) {
case 0:
return serviceImpl.getMsg(((Number)args[0]).intValue(), ((Number)args[1]).intValue());
case 1:
// 如果传入的实际对象是ServiceImplCglibProxy类型的,那么就会调用增强方法,而增强方法又会进而到intercept中,从而造成死循环
serviceImpl.show((String)args[0]);
return null;
}
} catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public Object invoke(int methodIndex, Object obj, Object[] args) throws InvocationTargetException {
// 强转成代理对象(ServiceImplCglibProxy)
ServiceImplCglibProxy serviceImplProxy = (ServiceImplCglibProxy)obj;
// 没有匹配就抛出异常
try {
// ProxyFastClass的索引顺序和TargetFastClass的索引顺序没有任何关系
switch (methodIndex) {
case 0:
return serviceImpl.getMsg(((Number)args[0]).intValue(), ((Number)args[1]).intValue());
case 1:
return serviceImpl.cglibGetMsg(((Number)args[0]).intValue(), ((Number)args[1]).intValue());
case 2:
serviceImpl.cglibShow((String)args[0]);
return null;
case 3:
serviceImpl.show((String)args[0]);
return null;
}
} catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}