Spring事务--this调用事务生效方案

本文探讨了Spring事务中this调用如何生效的问题,通过源码分析和实际例子展示了在启用@EnableAspectJAutoProxy(exposeProxy = true)的情况下,如何利用AopContext获取代理对象,实现方法间的事务隔离。示例代码展示了一种符合预期的事务管理方式,并给出了执行日志,以帮助读者理解事务处理机制。
部署运行你感兴趣的模型镜像

先结合源码部分看一下执行流程,然后说一下解决方案(本列使用cglib代理,jdk动态代理类似)
话不多说,先看一下代理拦截器方法:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;//这是咱们的目标类被各种切面封装后的代理对象
			boolean setProxyContext = false;
			Object target = null;
			TargetSource targetSource = this.advised.getTargetSource();
			try {
				if (this.advised.exposeProxy) {//很重要,设置了暴露代理才会执行下一步内容
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);//设置代理,并保存旧代理(1)
					setProxyContext = true;
				}
				// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//拦截器链
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {//没有拦截器
					// We can skip creating a MethodInvocation: just invoke the target directly.
					// Note that the final invoker must be an InvokerInterceptor, so we know
					// it does nothing but a reflective operation on the target, and no hot
					// swapping or fancy proxying.
					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 && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);//重新设置自己的代理类
				}
			}
		}

进一步解析(1)处的内容:

public final class AopContext {
	//使用threadlocal存储代理对象
	private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");


	private AopContext() {
	}

//可以在任何地方通过threadlocal获取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;
	}

//设置代理时,放到threadlocal中
	@Nullable
	static Object setCurrentProxy(@Nullable Object proxy) {
		Object old = currentProxy.get();
		if (proxy != null) {
			currentProxy.set(proxy);
		}
		else {
			currentProxy.remove();
		}
		return old;
	}
}

通过上面的源码分析,
1.开启暴露代理
2.我们可以直接通过AopContext 获取到Proxy对象,从而调用上面被各种切面织入的方法,进而实现代理.
理论不如实践,进行一下对比springboot环境:
开启暴露代理:@EnableAspectJAutoProxy(exposeProxy = true)

这一部分是事务不生效实例:


@Service
public class TranService{

    @Autowired
    private TranDao tranDao;

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void add() {
        User aaa = new User();
        aaa.setAge(22);
        aaa.setCtime(new Date());
        aaa.setUserId(2);
        aaa.setUserName("aaa");
        tranDao.save(aaa);
        try {
            b();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
    public void b() {
        User aaa = new User();
        aaa.setAge(33);
        aaa.setCtime(new Date());
        aaa.setUserId(3);
        aaa.setUserName("bbb");
        tranDao.save(aaa);
        throw new RuntimeException("111");
    }

}

说明一下上面的代码:按spring事务的规则,调用add()方法士应该开启一个事务,然后b()也加了事务,推断出想在b()调用时,开启单独事务,那么期望结果是a()提交,b()回滚

2019-07-17 21:26:04.623 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [com.example.demotran.tran.TranService.add]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Throwable
2019-07-17 21:26:04.623 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(76567159<open>)] for JPA transaction
2019-07-17 21:26:04.623 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@255421fa]
2019-07-17 21:26:04.639 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(76567159<open>)] for JPA transaction
2019-07-17 21:26:04.639 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
Hibernate: select user0_.user_id as user_id1_0_0_, user0_.age as age2_0_0_, user0_.ctime as ctime3_0_0_, user0_.user_name as user_nam4_0_0_ from t_user user0_ where user0_.user_id=?
2019-07-17 21:26:04.717 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(76567159<open>)] for JPA transaction
2019-07-17 21:26:04.717 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
Hibernate: select user0_.user_id as user_id1_0_0_, user0_.age as age2_0_0_, user0_.ctime as ctime3_0_0_, user0_.user_name as user_nam4_0_0_ from t_user user0_ where user0_.user_id=?
java.lang.RuntimeException: 111
	at com.example.demotran.tran.TranService.b(TranService.java:42)
	......
2019-07-17 21:26:04.717 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2019-07-17 21:26:04.717 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(76567159<open>)]
//两条数据都进行了插入,没有回滚
Hibernate: insert into t_user (age, ctime, user_name, user_id) values (?, ?, ?, ?)
Hibernate: insert into t_user (age, ctime, user_name, user_id) values (?, ?, ?, ?)
2019-07-17 21:26:04.733 DEBUG 7976 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(76567159<open>)] after transaction

看一下符合期望的编码:

package com.example.demotran.tran;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service
public class TranService{

    @Autowired
    private TranDao tranDao;

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void add() {
        User aaa = new User();
        aaa.setAge(22);
        aaa.setCtime(new Date());
        aaa.setUserId(2);
        aaa.setUserName("aaa");
        tranDao.save(aaa);
        try {
            TranService tranService = (TranService) AopContext.currentProxy();
            tranService.b();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
    public void b() {
        User aaa = new User();
        aaa.setAge(33);
        aaa.setCtime(new Date());
        aaa.setUserId(3);
        aaa.setUserName("bbb");
        tranDao.save(aaa);
        throw new RuntimeException("111");
    }

}

看一下执行日志:

//开启事务
2019-07-17 21:28:34.108 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [com.example.demotran.tran.TranService.add]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Throwable
2019-07-17 21:28:34.108 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(176172822<open>)] for JPA transaction
2019-07-17 21:28:34.124 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@58c34766]
2019-07-17 21:28:34.124 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(176172822<open>)] for JPA transaction
2019-07-17 21:28:34.124 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
Hibernate: select user0_.user_id as user_id1_0_0_, user0_.age as age2_0_0_, user0_.ctime as ctime3_0_0_, user0_.user_name as user_nam4_0_0_ from t_user user0_ where user0_.user_id=?
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(176172822<open>)] for JPA transaction
//挂起了当前事务
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Suspending current transaction, creating new transaction with name [com.example.demotran.tran.TranService.b]
//开启了一个新的实体管理器为事务操作
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(1157613382<open>)] for JPA transaction
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@1e1d8bf4]
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1157613382<open>)] for JPA transaction
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
Hibernate: select user0_.user_id as user_id1_0_0_, user0_.age as age2_0_0_, user0_.ctime as ctime3_0_0_, user0_.user_name as user_nam4_0_0_ from t_user user0_ where user0_.user_id=?
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction rollback
//回滚了事务
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Rolling back JPA transaction on EntityManager [SessionImpl(1157613382<open>)]
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(1157613382<open>)] after transaction
//重启了当前事务
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Resuming suspended transaction after completion of inner transaction
java.lang.RuntimeException: 111
	at com.example.demotran.tran.TranService.b(TranService.java:41)
	......
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2019-07-17 21:28:34.170 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(176172822<open>)]
//最终只有一条数据插入库中
Hibernate: insert into t_user (age, ctime, user_name, user_id) values (?, ?, ?, ?)
2019-07-17 21:28:34.186 DEBUG 7940 --- [  restartedMain] o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(176172822<open>)] after transaction

好了,今天就到这里.如果你有更好的方法,敬请留言,相互学习.

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值