参考:
用过Spring声明式事务的小伙伴肯定都知道,有这种一种场景:
REQUIRED
:支持当前事务,如果不存在则创建一个新事务)
上面2个场景的结果大家都是知道的,但是仔细想了下就有点蒙了。
Spring的声明式事务是通过Spring AOP代理实现的,默认情况下接口通过JDK代理实现、普通类通过CGLIB代理实现。
通常,我们理解的CGLIB代理,相当于就是通过一个子类在进行增强,那么testA()内部调用testB(),因为java多态的原因,也是调用子类(代理类)来完成,那么也是有增强的逻辑在的,那么为什么Sring中的CGLIB代理就不行,,也就是testB()的事务为什么不起作用?(我之前一直以为Spring AOP中的CGLIB代理和普通的CGLIB代理实现逻辑是一样的,,)
下面就带着这个问题来捋一捋Spring AOP中的CGLIB代理和我们认为的CGLIB有什么不同…
回顾下CGLIB代理生成代理类并调用目标方法流程。
执行下面的代码后,可以发现testA()内部调用testB()时,也触发了增强逻辑,也就是调用了代理类。控制台打印结果如下:
====== 执行方法开始:testA ======
====== 执行方法开始:testB ======
====== 执行方法结束:testB ======
====== 执行方法结束:testA ======
示例代码如下:
// 测试类
public class TestService {
public void testA() {
this.testB();
}
public void testB() {
}
}
// 生成CGLIB代理,并通过代理调用目标方法
public static void main(String[] args) {
// 代理类class文件存入本地磁盘,后面可以通过反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "doc");
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(TestService.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(String.format("====== 执行方法开始:%s ======", method.getName()));
Object object = methodProxy.invokeSuper(o, objects);
System.out.println(String.format("====== 执行方法结束:%s ======", method.getName()));
return object;
}
});
// 创建代理对象
TestService testService = (TestService) enhancer.create();
// 通过代理对象调用目标方法
testService.testA();
}
2.1中通过System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "doc");
保存了生成的代理类class文件,通过反编译看下代理类。
代理类继承了TestService,同时重写了testA()、testB()方法。
MethodInterceptor#intercept()
intercept()
方法中执行增强逻辑,并调用TestService#testA()
testA()
方法中调用了testB()
,因为继承的关系,又会调用代理类的testB()
,之后的逻辑和testA()
一样CGLIB代理类反编译后源码部分:
public class TestService$$EnhancerByCGLIB$$c4eb28c extends TestService implements Factory {
public final void testA() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$testA$0$Method, CGLIB$emptyArgs, CGLIB$testA$0$Proxy);
} else {
super.testA();
}
}
public final void testB() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$testB$1$Method, CGLIB$emptyArgs, CGLIB$testB$1$Proxy);
} else {
super.testB();
}
}
// 其他代码省略
}
一开始我的疑问也在这里,自己通过CGLIB代理模拟了代码增强的逻辑,同一类中内部方法调用也触发了代理类增强,怎么Spring AOP中的CGLIB代理怎么不触发呢。。
带着上面的疑问,通过debug观察一下代理类调用目标方法的流程。
(1)场景1:Controller#test()接口中只调用testA()方法
发现Controller中注入的不是service代理类实例,是service本身的实例,debug直接进入testA()方法,testA()调用testB()也是一样,不会触发代理逻辑
(2)场景2:Controller#test()接口中先调用testA()、再调用testB()
此时发现注入的service是CGLIB代理类的实例了,debug时进入org.springframework.aop.framework.CglibAopProxy#intercept()
中。
我们重点观察下这个方法到底干了啥,是否和我们2.2节中生成的CGLIB代理类逻辑一样:该方法主要流程:
通过查看源码,发现Spring AOP不是通过代理类来调用testA(),而是通过目标类进行反射调用,这样就可以解释了为什么testB()没有事务,因为不是直接通过代理类来完成调用的,也就无法触发代理逻辑了。
示例代码:
public class TransactionController {
@Autowired
private TransactionService transactionService;
@GetMapping("/test")
public void test() {
// 场景1,只调用testA()
transactionService.testA();
// 场景2,调用testA()、testB()
// transactionService.testB();
}
}
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
public void testA() {
transactionDao.insert(bean);
testA();
}
@Transactional(propagation = Propagation.REQUIRED)
public void testB() {
transactionDao.insert(bean);
int i = 1 / 0;
}
}
CglibAopProxy#intercept()源码如下:
// CglibAopProxy#intercept()
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 获取目标类,可能为null,延迟获取
// 例如示例中的TransactionService实例,非代理类实例
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 获取拦截器和动态代理拦截器,例如:事务拦截器
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 没有拦截器,直接通过反射调用目标类的目标方法
// 源码里注释也提到,这种情况可以直接调用目标类的目标方法,不需要创建MethodInvocation
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 假如必要的话适配方法的参数
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 通过反射调用目标类的目标方法
retVal = methodProxy.invoke(target, argsToUse);
} else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
(1)场景1:Controller#test()接口中只调用testA()方法
示例代码:
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.REQUIRED)
public void testA() {
transactionDao.insert("a");
testB();
int i = 1 / 0;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testB() {
transactionDao.insert("b");
}
}
由上面示例中可以猜测下,testB()的事务失效了,所以testA()和testB()在同一个事务中,testB()报错造成整个事务回滚了。
debug时仍然是进入org.springframework.aop.framework.CglibAopProxy#intercept()
中,该方法主要流程示例1中也简单分析过,这里主要是找到了拦截器:
TransactionInterceptor
)
CglibMethodInvocation
,并调用父类ReflectiveMethodInvocation
的proceed()
进行处理
proceed()
方法中,第一次会进入TransactionInterceptor#invoke()
进行事务相关逻辑的处理ReflectiveMethodInvocation#proceed()
方法中,第二次会执行CglibAopProxy#invokeJoinpoint()
方法,完成调用目标方法的操作invokeJoinpoint()
方法中,都是通过反射执行目标类的目标方法,而不是我之前所认为的代理类,所以就和3.1节 示例1的情况对应起来了,内部调用时事务确实是不起作用的到这里就比较清晰了,最终都是通过 代理类 完成逻辑增强,目标类 完成方法调用的,而不是代理类直接完成的,所以testA()调用testB()是不会触发代理逻辑的,相当于一个普通方法,和testA()在同一个事务中。
相关Spring源码如下:
// ReflectiveMethodInvocation#proceed()
public Object proceed() throws Throwable {
// currentInterceptorIndex第一次是-1
// 当完成事务准备工作后,就会进入这个分支
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 执行连接点,即目标方法
return invokeJoinpoint();
}
// currentInterceptorIndex+1
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
} else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
} else {
// 执行拦截器的invoke()方法
// 例如事务拦截器:TransactionInterceptor#invoke()
// testA()的事务就是从这里进入的
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
// CglibAopProxy#invokeJoinpoint()
protected Object invokeJoinpoint() throws Throwable {
// public方法
if (this.publicMethod) {
// 调用目标类的目标方法,注意这里的this.target,是真正的目标类,不是代理类
return this.methodProxy.invoke(this.target, this.arguments);
} else {
// 非public,设置method.setAccessible(true);,再进行反射调用
return super.invokeJoinpoint();
}
}
常见的解决方法:
既然内部调用不起作用,那就使用代理对象来调用:
方式一的示例代码如下:
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Autowired
private TransactionService transactionService;
@Transactional(propagation = Propagation.REQUIRED)
public void testA() {
transactionDao.insert("a");
// 注入的代理类来调用
transactionService.testB();
int i = 1 / 0;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testB() {
transactionDao.insert("b");
}
}
@EnableAspectJAutoProxy(exposeProxy = true)
注解,同时设置exposeProxy=true示例代码:
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.REQUIRED)
public void testA() {
transactionDao.insert("a");
// 获取代理类来调用testB()
TransactionService transactionService = (TransactionService) AopContext.currentProxy();
transactionService.testB();
int i = 1 / 0;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testB() {
transactionDao.insert("b");
}
}