class Service {
public void A() {
// 调用本类其他方法,事务失效
this.B();
}
@Transactional
public void B() {
}
}
如上代码,在同一个类中,非事务方法A调用事务方法B,会导致事务失效,可以采用AopContext.currentProxy().xxxxx()来保证事务生效。
无法切入的原因:
切入原理:创建代理类,在代理类中调用目标方法时进行切入。
上面代码,此时目标对象service,代理对象是Proxy_0,在同类Service中A方法调用B,本质是 this.B(),而不是Proxy_0.B()
其他解决办法
-
ApplicationContext.getBean()
-
在本类中注入自己
-
使用手动事务
注意事项:
Spring Boot需要在启动类加上以下注解
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
SSM需要xml文件配置
<aop:aspectj-autoproxy proxy-target-class="true"expose-proxy="true"/>
为什么AopContext可以解决同类方法AOP失效问题
AopContext类的源码如下
package org.springframework.aop.framework;
import org.springframework.core.NamedThreadLocal;
import org.springframework.lang.Nullable;
public final class AopContext {
// 维护了一个ThreadLocal,存放AOP代理类
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
private AopContext() {
}
public static Object currentProxy() throws IllegalStateException {
Object proxy = currentProxy.get();
if (proxy == null) {
throw new IllegalStateException(
"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " +
"ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.");
}
return proxy;
}
// 提供代理类set进ThreadLocal方法
@Nullable
static Object setCurrentProxy(@Nullable Object proxy) {
Object old = currentProxy.get();
if (proxy != null) {
currentProxy.set(proxy);
}
else {
currentProxy.remove();
}
return old;
}
}
Spring中创建动态代理有两种方式
-
JDK动态代理
-
cglib动态代理
jdk动态代理创建时JdkDynamicAopProxy中的invoke方法中调用AopContext存入代理类
image-20210621154601726.pngcglib动态代理CglibAopProxy在创建代理时也调用AopContext存入代理类
image-20210621155326867.png
网友评论