spring framework

AopContext.currentProxy()解决同类中调用

2021-06-21  本文已影响0人  markeNick
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()

其他解决办法

注意事项:

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动态代理创建时JdkDynamicAopProxy中的invoke方法中调用AopContext存入代理类

image-20210621154601726.png

cglib动态代理CglibAopProxy在创建代理时也调用AopContext存入代理类


image-20210621155326867.png
上一篇下一篇

猜你喜欢

热点阅读