【spring事务】 --- exposeProxy属性的作用 --- 同一个对象里方法间调用事务传播行为生效的方法

源码地址:https://github.com/nieandsun/spring-study


1 先看现象

有如下代码,调用addUserAndSalary方法,t_user表和t_salary表哪个表里会被插入数据呢???

@Service
public class UserServiceImpl implements UserService {


    @Autowired
    private UserDao userDaoImpl;

    @Autowired
    private SalaryDao salaryDao;

    /***
     * 插入用户信息和该用户的薪资信息
     * @param username
     * @param salary
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addUserAndSalary(String username, BigDecimal salary) {
        //按照生日等生成一个14位的员工编号
        String account = "20191218000001";

        //往员工表里插入该用户的用户信息
        userDaoImpl.saveUserInfo(username, account);
		
        addSalary(account, salary);
        //((UserService)AopContext.currentProxy()).addSalary(account,salary);
        int i = 1 / 0;
    }


    /***
     * 插入用户的薪资信息
     * @param account
     * @param salary
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void addSalary(String account, BigDecimal salary) {
        salaryDao.addSalaryInfo(account, salary);
    }
}

先来分析一下:

(1)假如看过我《【spring事务前置知识】事务的传播行为》那篇文章,肯定会得到如下答案:

  • 父级方法(addUserAndSalary)会发生回滚 —> t_user表里无法插入数据
  • 子级方法(addSalary)不会发送回滚 —> t_salary表里会插入数据

(2)但是再一想不对啊,在addUserAndSalary里调用addSalary方法,相当于this.addSalary(…),这不就相当于直接在addUserAndSalary里累代码么? 也就是说addSalary应该是addUserAndSalary方法的一部分,显然两个表里都不可能插入数据。

跑一下代码就可以轻松的验证,(2)分析的是正确的。

那如何在同一个对象里让事务传播行为生效呢???这里就要用到exposeProxy属性了。


2 从源码看一下exposeProxy在事务源码中的逻辑

上篇文章《【spring事务源码学习】— 目标方法调用流程核心源码解读》的2.1其实就已经见到exposeProxy属性了,这里截取2.1中关于exposeProxy属性的源码如下:
所在类:JdkDynamicAopProxy

//如果aop的exposeProxy设置的为true(将代理的目标对象暴露出来),这个if语句就能进去
if (this.advised.exposeProxy) {
	// Make invocation available if necessary.
	oldProxy = AopContext.setCurrentProxy(proxy);
	setProxyContext = true;
}

这段代码具体是什么意思呢?其实就是将当前代理对象放到AopContext对象中,AopContext的源码如下:

public final class AopContext {
	//线程对象
	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.");
		}
		return proxy;
	}
	//将代理对象set到AopContext中的线程对象里
	@Nullable
	static Object setCurrentProxy(@Nullable Object proxy) {
		Object old = currentProxy.get();
		if (proxy != null) {
			currentProxy.set(proxy);
		}
		else {
			currentProxy.remove();
		}
		return old;
	}

}

可以看到AopContext对像其实比较简单,它就是里面封装了一个线程对象,如果你将exposeProxy属性设置为true时,目标方法被调用时,就会将当前的目标对象的代理对象存入到其所持有的ThreadLocal对象里。

知道了AopContext对象和exposeProxy属性的原理,其实就可以很轻松的 解决同一个对象里方法间调用事务传播行为生效的问题了。


3 解决方式

其实很简单,只需要做两件事:
(1)在启动配置类中加上@EnableAspectJAutoProxy(exposeProxy = true)注解,注意该注解正是开启AOP的注解
(2)在addUserAndSalary方法里通过AopContext对象获取到UserService的代理对象,并利用代理对象调用addSalary方法,即使用1中addSalary(account, salary);语句下的注释语句:

 ((UserService)AopContext.currentProxy()).addSalary(account,salary);

当然如果是springboot项目,只需要在启动类上加上@EnableAspectJAutoProxy(exposeProxy = true)注解就可以了。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值