Spring事物源码解析

案例

依赖导入

在 Spring Boot 中使用事务,需要导入 MySQL 依赖:

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

导入依赖之后会自动注入DataSourceTransactionManager 无需其他配置就可使用@Transactional注解进行事务的使用了。

事物使用

@Service
public class UserServiceImpl implements UserService{

    @Resource
    private UserMapper userMapper;

    @Override
    @Transactional
    public void insertUser2(User user) throws Exception {
        //插入用户信息
        userMapper.insertUser(user);
        //手动抛出异常
        throw new SQLException("数据库异常");
    }

}

通过@Transactional正常事物功能就实现了,如上throw new SQLException("数据库异常"); 事物不会回滚,原因如下

注意

Spring Boot 默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。比如上面例子中抛出的 RuntimeException,可完成回滚,而抛出 SQLException,则无法回滚。针对非检测异常,如果要进行事务回滚,可以在 @Transactional 注解中使用 rollbackFor 属性指定异常,比如 @Transactional(rollbackFor = Exception.class),这样就没有问题了。这也在告诉我们,实际项目开发中,一定要指定异常。

源码跟踪

跟踪源码之后,事物实现主要分三部分:

  1. 有@Transactional注解的类会生成代理类
  2. 调用@Transactional注解的方法,代理类会管理自己的数据库连接,放入到线程上下文
  3. 调用mybatis方法会从线程上下文获取数据库连接

代理类

如下是创建@Transactional注解的类的创建过程

1. bean的创建过程,参考spring之DefaultListableBeanFactory的bean的创建过程

2. 主要是BeanPostProcessor的子类AnnotationAwareAspectJAutoProxyCreator的初始化之后业务逻辑方法postProcessAfterInstantiation,postProcessAfterInstantiation内部会调用org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary方法,wrapIfNecessary主要判断是否有复合要求的Advice,如果有就创建代理类。

3. 判断是否有符合要求的Advisor,Spring advisors(org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor) 和  AspectJ生成的Advisors(@Aspect注解)

4. 事物TransactionAttributeSourcePointcut切面判断制定方法是否能有@Transactional注解,如果有@Transactional注解,解析注解生成TransactionAttribute对象

 如上就是spring生成bean的时候,判断是否包含@Transactional,如果包含就生成代理proxy。

方法拦截

TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor,调用代理类的方法,都会调用org.springframework.transaction.interceptor.TransactionInterceptor#invoke,代码如下

 我们主要看invokeWithinTransaction方法,代码如下

1. 内部会调用org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction获取或创建一个事物,获取数据库Connection,设置成AutoCommit=false,把ConnectionHolder绑定到ThreadLocal。

2.执行业务target类的方法,执行数据操作,会从ThreadLocal中获取ConnectionHolder中的Connection执行sql

3.如果业务代码执行失败,回滚事物

4.如果业务代码执行成功,提交事物

 

事务传播类型

 重点讲一下AbstractPlatformTransactionManager#getTransaction,事务传播类型的功能就是在这里实现的。有事物存在就执行handleExistingTransaction方法,没有事物存在就执行下面的代码。

handleExistingTransaction代码如下

/**
	 * Create a TransactionStatus for an existing transaction.
	 */
	private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {

		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}

		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}

		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction, creating new transaction with name [" +
						definition.getName() + "]");
			}
			SuspendedResourcesHolder suspendedResources = suspend(transaction);
			try {
				return startTransaction(definition, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error beginEx) {
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
		}

		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			if (!isNestedTransactionAllowed()) {
				throw new NestedTransactionNotSupportedException(
						"Transaction manager does not allow nested transactions by default - " +
						"specify 'nestedTransactionAllowed' property with value 'true'");
			}
			if (debugEnabled) {
				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
			}
			if (useSavepointForNestedTransaction()) {
				// Create savepoint within existing Spring-managed transaction,
				// through the SavepointManager API implemented by TransactionStatus.
				// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
				DefaultTransactionStatus status =
						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				// Nested transaction through nested begin and commit/rollback calls.
				// Usually only for JTA: Spring synchronization might get activated here
				// in case of a pre-existing JTA transaction.
				return startTransaction(definition, transaction, debugEnabled, null);
			}
		}

		// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
		if (debugEnabled) {
			logger.debug("Participating in existing transaction");
		}
		if (isValidateExistingTransaction()) {
			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
				Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
				if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
					Constants isoConstants = DefaultTransactionDefinition.constants;
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] specifies isolation level which is incompatible with existing transaction: " +
							(currentIsolationLevel != null ?
									isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
									"(unknown)"));
				}
			}
			if (!definition.isReadOnly()) {
				if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] is not marked as read-only but existing transaction is");
				}
			}
		}
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
	}

如上代码所示:

  • PROPAGATION_NEVER :如果有事物存在就报错
  • PROPAGATION_NOT_SUPPORTED :如果存在事物就挂起事物
  • PROPAGATION_REQUIRES_NEW :开启一个新事物,老事物挂起
  • PROPAGATION_NESTED:嵌套事物通过数据库的savepoint功能实现

挂起事物:实现很简单,就是解绑上下文中的ConnectionHolder,保存到内存中,等操作完成,再从内存中还原事物状态和ConnectionHolder。

Spring事务核心类

Spring事务的构成,基本有三个部分,事务属性的定义,事务对象及状态信息的持有,事务管理器的处理。

事务属性的定义

  1. TransactionDefinition 事务定义(传播属性,隔离属性,timeout等)
  2. TransactionAttribute 事务属性接口 (继承TransactionDefinition),常用的实现是RuleBasedTransactionAttribute
  3. TransactionAttributeSource 事务属性数据源,可以根据method和Class获取TransactionAttribute(注解方式的实现为AnnotationTransactionAttributeSource,编程方式的实现为NameMatchTransactionAttributeSource)

事务对象及状态信息的持有

  1. Transaction 事务对象,由具体的事务管理器实现返回
  2. TransactionStatus 事务状态,持有事务对象以及事务本身的属性及状态,每次事务方法执行前,生成一个新的TransactionStatus,但如果不需要创建新事务,则持有的事务和上层一致
  3. TransactionInfo 事务信息,持有事务属性,事务状态,事务连接点(方法路径),事务管理器,每次事务方法执行,生成新的TransactionInfo,并且绑定到当前线程(ThreadLocal),同时持有上一事务信息对象,形成链式结构。

事务管理器的处理

  1. PlatformTransactionManager事务管理接口,AbstractPlatformTransactionManager为其抽象实现(实现了getTransaction,commit,rollback模板方法),常用的实现如JDBC的事务管理实现DataSourceTransactionManager

1.TransactionInterceptor
事务处理增强
2.TransactionAttributeSourcePointcut
事务处理属性源切点
3.TransactionAttributeSourceAdvisor
事务处理属性源切面

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值