spring事务实现原理----源码总结

通常我们在日常开发中在配置类上增加@EnableTransactionManagement注解,在业务开发时,只要在业务类上或者方法上加@Transactional注解,spring就会帮我们做好创建数据库连接,设置相关的数据隔离级别,超时,提交或者回滚等操作,而我们只要专注业务开发,做好配置即可。那么spring是如何做到处理各种复杂情况呢?

首先我们来看看这两个注解为我们做了哪些事情:

@EnableTransactionManagement

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    ....
}

该注解导入了TransactionManagementConfigurationSelector类,这个实现了ImportSelector接口,spring在启动的时候就会执行selectImports()方法,并将返回的AutoProxyRegistrar和ProxyTransactionManagementConfiguration类进行解析并注册到spring容器中去;

而AutoProxyRegistrar这个类又实现ImportBeanDefinitionRegistrar类,故spring又会执行registerBeanDefinitions()方法,这里会进行一步尤为重要的操作,向spring注册InfrastructureAdvisorAutoProxyCreator这个BeanPostProcessor,Bean才可以进行AOP;

另外ProxyTransactionManagementConfiguration类中定义了三个bean:

BeanFactoryTransactionAttributeSourceAdvisor:advisor

AnnotationTransactionAttributeSource:定义了一个pointcut(判断某个类或者某个方法上是否加了@Transactional注解),存在就解析@Transactional注解的信息并封装为RuleBasedTransactionAttribute对象,并且会被设置到advisor里

TransactionInterceptor:定义了advice,即代理逻辑(事务创建 提交 回滚等等操作)

spring bean在初始化后,会先从beanFactory中找到所有的advisor,然后遍历所有的advisor,判断是否属于PointcutAdvisor类,再通过TransactionAttributeSourcePointcut判断目标类上是否存在@Transactional以及方法上是否存在@Transactional,只要存在就将advisor缓存到集合中并返回,并为目标类创建代理对象。

1、test()调用abc()用的是同一个数据库连接,此时sql执行要么全部提交,要么全部失败

        如果设置transactionManager.setGlobalRollbackOnParticipationFailure(false),abc()中sql执行错误,并且test()方法中捕获异常;那么此时就会出现test()方法中sql执行成功提交,abc()中执行失败回滚

@Component
public class UserService {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Autowired
	private UserService userService;

	@Transactional(rollbackFor = Exception.class)
	public void test() {
		jdbcTemplate.execute("insert into goods(id, stock, name, detail) values('2', '1', 'apple', '200')");
		userService.abc("test");
	}

	@Transactional
	public void abc(String str) {
		jdbcTemplate.execute("insert into goods(id, stock, name, detail) values('3', '3', 'xiaomi', '100')");
		throw new NullPointerException();
	}
}

 2、abc()方法设置@Transactional(propagation = Propagation.REQUIRES_NEW),就会新创建一个数据库连接;此时abc()抛出异常,如果test()没有捕获,则都会回滚,否则,test()提交,abc()回滚。

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void abc(String str) {
	jdbcTemplate.execute("insert into goods(id, stock, name, detail) values('3', '3', 'xiaomi', '100')");
	throw new NullPointerException();
}

那么spring是如何新建数据库连接,在test()调用abc()执行完成以后如何拿到之前的连接的呢?

当调用代理类的方法时,就会执行TransactionInterceptor#invoke()方法:

public Object invoke(MethodInvocation invocation) throws Throwable {
	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    // 执行创建数据库连接,设置隔离级别,回滚或提交等操作
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
		@Override
		@Nullable
		public Object proceedWithInvocation() throws Throwable {
			// 执行后续的Interceptor,以及被代理的方法
			return invocation.proceed();
		}
		@Override
		public Object getTarget() {
			return invocation.getThis();
		}
		@Override
		public Object[] getArguments() {
			return invocation.getArguments();
		}
	});

1、拿到@Transactional注解上所有属性信息(已经封装为一个对象)

2、拿到spring容器中设置好的spring事务管理器(TransactionManager)

3、拿到当前正在执行的被代理类的方法名称

4、创建事务:test():先获取是否已经存在一个事务,而且propagation = Propagation.REQUIRES则返回当前事务,否则先挂起当前线程的资源(缓存到一个资源对象中,但此时为空),再去创建事务,并设置相关属性信息,设置autoCommit=false,把当前事务存到ThreadLocal(DataSource对象为key,数据库连接对象为value)中

5、执行abc(),假设此时propagation = Propagation.REQUIRES_NEW,先挂起当前线程的资源并缓存到挂起资源对象中,再去创建一个新事物(属性设置等等);当执行完abc()方法后,无论是提交还是回滚,都会关闭新建的数据库连接,并恢复现场,把挂起资源对象属性重新设置到当前线程的ThreadLocal中去。

6、最后test()方法执行完成以后,也会关闭数据连接等操作

注意:如果程序员设置了TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()表示本来可以提交的事物,强制回滚。NOT_SUPPORT,使用jdbc自己的连接。

spring事务面试题记录:

被问到spring事务默认对哪些异常进行回滚? 特此记录:

开启事务后TransactionInterceptor.invoke()方法会执行下面的核心方法

进入invokeWithinTransaction():

进入completeTransactionAfterThrowing(): 

可以看出抛出了三中类型异常:TransactionSystemException|RuntimeException|Error,而TransactionSystemException也是RuntimeException的子类。 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值