• AopContext对象原理


    假如在一个service中,有A()和B()两个方法,假如A方法没有添加事务注解,B方法有添加事务注解,如果在A方法中调用B方法,那B方法并不会以事务方式运行,这是因为,在调用A()方法的时候,会判断这个方法是否需要有通知方法(或者说是否有切面作用于方法上)如果没有,就不会以代理对象的方式调用运行,在A调用B的时候,其实就是内部方法调用,所以B方法的事务并不会生效

    如果我们要让B方法的事务生效,那就可以在A()方法中,调用B方法的时候,通过AopContext.currentProxy().B()的方式来调用,这样B方法的事务就会生效

    应用

    //@Transactional
    public void testNoTrans(){
        System.out.println("这是非事务方法");
        testTx();
    }
    
    @Transactional(rollbackFor = NullPointerException.class,propagation = Propagation.REQUIRES_NEW)
    public void testTx() {
        
        OperChannel updateOperChannel = new OperChannel();
        updateOperChannel.setOperChannelId("7200");
        updateOperChannel.setOperChannelName("河南微信-02");
        operchannelDao.updateOperChannel(updateOperChannel);
        System.out.println("测试信息");
    
        // otherService.testUpdateOther();
        OperChannel operChannelById = operchannelDao.getOperChannelById(1);
        System.out.println(operChannelById.toString());
        System.out.println(operChannelById.getOperChannelName());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述
    这里可以看到,在调用的时候,发现此时的OperChannelService只是一个简单的对象,也可以通过在代码中,加上一行 “int i = 10/0”,来看事务是否回滚,此时事务是不会回滚的

    如果把调用testTx()的代码,改成:

    OperChannelService operChannelService = (OperChannelService) AopContext.currentProxy();
    operChannelService.testTx();
    
    • 1
    • 2

    此时会发现,事务已经生效了
    在这里插入图片描述

    AopContext既然可以获取到代理对象,那就表示,一定在某一个地方,把代理对象设置到了内存中

    源码

    可以通过org.springframework.aop.framework.AopContext#currentProxy获取到当前的代理对象,然后通过这个代理对象,调用目标方法
    这样,就可以以事务方法运行

    private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
    
    
    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.");
    	}
    	return proxy;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过上面的方法,可以发现,在调用currentProxy()方法的时候,实际上是尝试去threadLocal中获取一个对象,然后返回,这里既然可以在这里取到,那一定是在某一个地方,向ThreadLocal中设置了一个对象

    设置代理对象到threadlocal中:

    	org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
    在目标方法被调用的时候,会被这里的拦截器所拦截,在这里的intercept()方法中,有一段及其重要的代码
    
    if (this.advised.exposeProxy) {
    	// Make invocation available if necessary.
    	oldProxy = AopContext.setCurrentProxy(proxy);
    	setProxyContext = true;
    }
    
    @Nullable
    static Object setCurrentProxy(@Nullable Object proxy) {
    	Object old = currentProxy.get();
    	if (proxy != null) {
    		currentProxy.set(proxy);
    	}
    	else {
    		currentProxy.remove();
    	}
    	return old;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    我们知道,在使用AopContext对象时,一定有一个前提,就是要在配置类上加上@EnableAspectJAutoProxy(exposeProxy = true),并且把exposeProxy设置TRUE,是因为在上面这段代码中,会判断这个参数是否为TRUE,只有在为true的情况下,才会在AopContext对象中,把当前的proxy代理对象设置到ThreadLocal中

    所以,以上就是AopContext对象的原理

    总结

    所以,总结来看,如果使用了AopContext对象,针对上面在非事务方法中,调用事务方法的流程是这样的:

    1. 在方法被调用的时候,会被org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept所拦截
    2. 然后根据exposeProxy()是否被设置为true,来决定,是否要向ThreadLocal中设置当前的代理对象
    3. 在非事务方法中,通过AopContext.currentProxy()获取代理对象时,获取的是在ThreadLocal中写入的代理对象
    4. 然后以代理对象(事务方式运行)

    如果没有使用AopContext对象:

    1. 在方法被调用的时候,会被org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept所拦截
    2. 然后根据exposeProxy()是否被设置为true,来决定,是否要向ThreadLocal中设置当前的代理对象
    3. 在非事务方法中,没有使用AopContext.currentProxy(),所以,在调用testTx()方法的时候,只是一个普通方法调用
  • 相关阅读:
    [英雄星球六月集训LeetCode解题日报] 第30日 拓扑排序
    【C++】构造函数与析构函数用途 ( 代码示例 - 构造函数与析构函数用途 )
    京东云联合Forrester咨询发布混合云报告 云原生成为驱动产业发展新引擎
    List,Set,Map集合总结
    20. 【Linux教程】emacs 编辑器
    CSS 三栏布局
    Selenium+Python系列(二) - 元素定位那些事
    url在api测试工具可以访问,但在浏览器不能访问
    预训练模型的多任务主动学习
    左神算法(一)上修改版
  • 原文地址:https://blog.csdn.net/CPLASF_/article/details/125267381