java-5.6 spring-tx

336 阅读11分钟

需求

Spring-tx模块负责在spring框架中实现事务管理功能。以aop切面的方式将事务注入到业务代码中,并实现不同类型的事务管理器。

设计

在Spring中,事务有两种实现方式:

  • 编程式事务管理: 编程式事务管理使用TransactionTemplate可实现更细粒度的事务控制。
  • 声明式事务管理: 基于Spring AOP实现。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单(尤其是配合spring boot自动配置,可以说是精简至极!),且大部分业务都可以满足,推荐使用。 其实不管是编程式事务还是声明式事务,最终调用的底层核心代码是一致的。

api

事务的属性

要保证事务的ACID特性,spring给事务定义了6个属性,对应于声明式事务注解(org.springframework.transaction.annotation.Transactional)@Transactional(key1=,key2=...)

  • 事务名称:用户可手动指定事务的名称,当多个事务的时候,可区分使用哪个事务。对应注解中的属性value、transactionManager
  • 隔离级别: 为了解决数据库容易出现的问题,分级加锁处理策略。 对应注解中的属性isolation
  • 超时时间: 定义一个事务执行过程多久算超时,以便超时后回滚。可以防止长期运行的事务占用资源.对应注解中的属性timeout
  • 是否只读:表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务.对应注解中的属性readOnly
  • 传播机制: 对事务的传播特性进行定义,共有7种类型。对应注解中的属性propagation
  • 回滚机制:定义遇到异常时回滚策略。对应注解中的属性rollbackFor、noRollbackFor、rollbackForClassName、noRollbackForClassName

传播机制

org.springframework.transaction包下有一个事务定义接口TransactionDefinition,定义了7种事务传播机制,很多人对传播机制的曲解从概念开始,所以特地翻译了一下源码注释如下:

  1. PROPAGATION_REQUIRED 支持当前事务;如果不存在,创建一个新的。类似于同名的EJB事务属性。这通常是事务定义的默认设置,通常定义事务同步作用域。

  2. PROPAGATION_SUPPORTS 支持当前事务;如果不存在事务,则以非事务方式执行。类似于同名的EJB事务属性。 注意:

对于具有事务同步的事务管理器,PROPAGATION_SUPPORTS与没有事务稍有不同,因为它可能在事务范围内定义了同步。因此,相同的资源(JDBC的Connection、Hibernate的Session等)将在整个指定范围内共享。注意,确切的行为取决于事务管理器的实际同步配置!

小心使用PROPAGATION_SUPPORTS!特别是,不要依赖PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW,在PROPAGATION_SUPPORTS范围内(这可能导致运行时的同步冲突)。如果这种嵌套不可避免,请确保适当地配置事务管理器(通常切换到“实际事务上的同步”)。

  1. PROPAGATION_MANDATORY 支持当前事务;如果当前事务不存在,抛出异常。类似于同名的EJB事务属性。 注意: PROPAGATION_MANDATORY范围内的事务同步总是由周围的事务驱动。

  2. PROPAGATION_REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起当前事务。类似于同名的EJB事务属性。

注意:实际事务挂起不会在所有事务管理器上开箱即用。这一点特别适用于JtaTransactionManager,它需要TransactionManager的支持。

PROPAGATION_REQUIRES_NEW范围总是定义自己的事务同步。现有同步将被挂起并适当地恢复。

  1. PROPAGATION_NOT_SUPPORTED 不支持当前事务,存在事务挂起当前事务;始终以非事务方式执行。类似于同名的EJB事务属性。

注意:实际事务挂起不会在所有事务管理器上开箱即用。这一点特别适用于JtaTransactionManager,它需要TransactionManager的支持。

事务同步在PROPAGATION_NOT_SUPPORTED范围内是不可用的。现有同步将被挂起并适当地恢复。

  1. PROPAGATION_NEVER 不支持当前事务;如果当前事务存在,抛出异常。类似于同名的EJB事务属性。

注意:事务同步在PROPAGATION_NEVER范围内不可用。

  1. PROPAGATION_NESTED 如果当前事务存在,则在嵌套事务中执行,如果当前没有事务,类似PROPAGATION_REQUIRED(创建一个新的)。EJB中没有类似的功能。

注意:实际创建嵌套事务只对特定的事务管理器有效。开箱即用,这只适用于 DataSourceTransactionManager(JDBC 3.0驱动)。一些JTA提供者也可能支持嵌套事务。

下面是隔离级别的表格:

image.png 注意:JtaTransactionManager的类注释上说:Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with a JTA TransactionManager being registered." 这是片面的,只是说JTA TransactionManager支持挂起,并没有说DataSourceTransactionManager不支持。经过第四节实测,发现完全是支持的。网上很多说REQUIRES_NEW、NOT_SUPPORTED必须要JTA TransactionManager才行的完全是错误的说法。

usage

  • 声明式事务管理 如下图,只需要在service.impl层,业务方法上添加@Transactional注解,定义事务的传播机制为REQUIRED(不写这个参数,默认就是REQUIRED),遇到Exception异常就一起回滚。

REQUIRED传播机制下:存在加入事务,不存在创建新事务。保证了当前方法中的所有数据库操作都在一个物理事务中,当遇到异常时会整个业务方法一起回滚。

    @Transactional(propagation= Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void addUserBalanceAndUser(String name, BigDecimal balance) {
        log.info("[addUserBalanceAndUser] begin!!!");
        //1.新增用户
        userService.addUser(name);
        //2.新增用户余额
        UserBalance userBalance = new UserBalance();
        userBalance.setName(name);
        userBalance.setBalance(new BigDecimal(1000));
        this.addUserBalance(userBalance);
        log.info("[addUserBalanceAndUser] end!!!");
    }
  • 编程式事务管理 使用Spring推荐的transactionTemplate。用自动配置类配置好了TransactionTemplate这个类型的bean.使用的时候直接注入bean使用即可(当然老式的xml配置也是一样的)。如下:
    @Override
    public void addUserBalanceAndUserWithinTT(String name, BigDecimal balance) {
        //实现一个没有返回值的事务回调
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    log.info("[addUserBalanceAndUser] begin!!!");

                    //1.新增用户
                    userService.addUser(name);
                    //2.新增用户余额
                    UserBalance userBalance = new UserBalance();
                    userBalance.setName(name);
                    userBalance.setBalance(new BigDecimal(1000));
                    userBalanceRepository.insert(userBalance);
                    log.info("[addUserBalanceAndUser] end!!!");
                    //注意:这里catch住异常后,设置setRollbackOnly,否则事务不会滚。当然如果不需要自行处理异常,就不要catch了
                } catch (Exception e) {
                    // 异常回滚
                    status.setRollbackOnly();
                    log.error("异常回滚!,e={}",e);
                }

            }
        });
    }

实现

springboot自动装配

1. spring.factories中指定TransactionAutoConfiguration自动配置类

在/META-INF/spring.factories中配置文件中查找 org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

在TransactionAutoConfiguration类中的AutoProxyConfiguration中注有@EnableTransactionManagement这个注解

		@Configuration(proxyBeanMethods = false)
		@EnableTransactionManagement(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		public static class CglibAutoProxyConfiguration {//默认是启用CglibAutoProxyConfiguration

		}

2. import装载

查看EnableTransactionManagement注解, TransactionManagementConfigurationSelector继承自AdviceModeImportSelector实现了ImportSelector接口。

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

最终会执行selectImports方法导入需要加载的类,我们只看proxy模式下,载入了AutoProxyRegistrar、ProxyTransactionManagementConfiguration2个类。

protected String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
        case PROXY:
            return new String[] {AutoProxyRegistrar.class.getName(),
                    ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            return new String[] {determineTransactionAspectClass()};
        default:
            return null;
    }
}
  • AutoProxyRegistrar:给容器中注册一个 AnnotationAwareAspectJAutoProxyCreator aop的动态代理创建者组件;利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
  • ProxyTransactionManagementConfiguration:就是一个配置类,定义了事务增强器。 AutoProxyRegistrar最终生成的是CGLIB代理类. 代理类执行时调用的方法则是ProxyTransactionManagementConfiguration配置的.

transactionAdvisor()方法事务织入,定义了一个advisor,设置事务属性、设置事务拦截器

TransactionInterceptor、设置顺序。核心就是事务拦截器TransactionInterceptor。

TransactionInterceptor使用通用的spring事务基础架构实现“声明式事务”,继承自TransactionAspectSupport类(该类包含与Spring的底层事务API的集成),实现了MethodInterceptor接口。

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(//定义事务增强器
			TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
		advisor.setTransactionAttributeSource(transactionAttributeSource);
		advisor.setAdvice(transactionInterceptor);
		if (this.enableTx != null) {
			advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {//定义基于注解的事务属性资源
		return new AnnotationTransactionAttributeSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {//定义事务拦截器
		TransactionInterceptor interceptor = new TransactionInterceptor();
		interceptor.setTransactionAttributeSource(transactionAttributeSource);
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}

}

TransactionInterceptor复写MethodInterceptor接口的invoke方法,并在invoke方法中调用了父类TransactionAspectSupport的invokeWithinTransaction()方法

至此,springboot的自动装配已经完成,后面代码执行注解了@Transactional的方法,会调用TransactionAspectSupport的invokeWithinTransaction()方法

创建事务

事务执行逻辑全部在TransactionAspectSupport的invokeWithinTransaction()方法中。

  1. 从beanfactory中取到加载进来的TransactionManager(其继承自AbstractPlatformTransactionManager,由springboot自动加载,默认实现有spring-orm的JpaTransactionManager和spring-jdbc的DataSourceTransactionManager),
        TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);//获取@Transactional注解属性
		final TransactionManager tm = determineTransactionManager(txAttr);//AbstractPlatformTransactionManager
        ...
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);//调用tm.getTransaction(txAttr);

        Object retVal;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            completeTransactionAfterThrowing(txInfo, ex);// 根据事务定义的,该异常需要回滚就回滚,否则提交事务
            throw ex;
        }
        commitTransactionAfterReturning(txInfo);////返回结果之前提交事务
        return retVal;

createTransactionIfNecessary方法会先获取事务对象,然后将事务对象放入threadLocal对象中transactionInfoHolder

status = tm.getTransaction(txAttr);
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);//调用txInfo.bindToThread();将事务对象放入threadLocal对象中transactionInfoHolder
  1. 模板方法创建事务,并根据事务传播属性开启事务 调用AbstractPlatformTransactionManager的getTransaction方法,模板方法doGetTransaction创建JdbcTransactionObjectSupport对象,放入DataSource数据源(DataSource数据源也是由springboot启动加载指定,默认由HikariDataSource,用户可以在application.yaml中指定spring.datasource.type)
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException {

		// Use defaults if no transaction definition given.
		TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

		Object transaction = doGetTransaction();
		boolean debugEnabled = logger.isDebugEnabled();
        // 如果当前已经存在事务
		if (isExistingTransaction(transaction)) {
           // 根据不同传播机制不同处理
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(def, transaction, debugEnabled);
		}
        // 超时不能小于默认值
		// Check definition settings for new transaction.
		if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
		}
        // 当前不存在事务,传播机制=MANDATORY(支持当前事务,没事务报错),报错
		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}// 当前不存在事务,传播机制=REQUIRED/REQUIRED_NEW/NESTED,这三种情况,需要新开启事务,且加上事务同步
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
				return startTransaction(def, transaction, debugEnabled, suspendedResources);//调用模板方法doBegin(transaction, definition);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {// 是否需要新开启同步
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + def);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
		}
	}

DataSourceTransactionManager的doGetTransaction实现(JpaTransactionManager类似):

    protected Object doGetTransaction() {
        DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();
        txObject.setSavepointAllowed(this.isNestedTransactionAllowed());
        //ConnectionHolder中放入DataSource
        ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());
        txObject.setConnectionHolder(conHolder, false);
        return txObject;
    }

DataSourceTransactionManager的doBegin实现(JpaTransactionManager类似):获取connection,设置连接属性, 绑定connection持有者到当前线程

con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);//设置事务的隔离级别->con.setTransactionIsolation(definition.getIsolationLevel());
txObject.setReadOnly(definition.isReadOnly());//设置只读
if (con.getAutoCommit()) {//设置自动提交
    txObject.setMustRestoreAutoCommit(true);
    if (logger.isDebugEnabled()) {
        logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
    }
    con.setAutoCommit(false);
}
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    txObject.getConnectionHolder().setTimeoutInSeconds(timeout);// 设置超时秒数
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
    TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());// 绑定connection持有者到当前线程
}
  1. 通过反射真正执行@Transactional注解方法中的代码。 根据事务定义的,该异常需要回滚就回滚,否则提交事务;若未异常则正常提交返回
retVal = invocation.proceedWithInvocation();

commit

  1. 入口: TransactionAspectSupport的invoke方法最后会执行commitTransactionAfterReturning进行事务提交,调用AbstractPlatformTransactionManager的commit接口
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
			}
			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
		}
	}
	public final void commit(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {// 如果事务已完结,报错无法再次提交
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		if (defStatus.isLocalRollbackOnly()) {/ 如果事务明确标记为回滚
			if (defStatus.isDebug()) {
				logger.debug("Transactional code has requested rollback");
			}
			processRollback(defStatus, false);//执行回滚
			return;
		}
        //如果不需要全局回滚时提交 且 全局回滚
		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}//执行回滚
			processRollback(defStatus, true);
			return;
		}
         // 执行提交事务
		processCommit(defStatus);
	}
  1. processCommit会进行一些事务传播机制的处理操作,然后调用模板方法doCommit方法,下列为DataSourceTransactionManager的实现,执行connection的commit方法
	protected void doCommit(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Committing JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.commit();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not commit JDBC transaction", ex);
		}
	}

rollback

  1. 入口: TransactionAspectSupport的invoke方法, 执行@Transactional方法异常后会执行completeTransactionAfterThrowing决定是否回滚,调用AbstractPlatformTransactionManager的rollback接口
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
						"] after exception: " + ex);
			}
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());//注解了回滚操作
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
			}
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				try {
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
			}
		}
	}
public final void rollback(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		processRollback(defStatus, false);
	}
  1. processRollback会进行一些事务传播机制的处理操作,然后调用模板方法doRollback方法,下列为DataSourceTransactionManager的实现,执行connection的rollback方法
	@Override
	protected void doRollback(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.rollback();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
		}
	}

原理

jdbc

connection.commit/rollback

事务

参考

www.cnblogs.com/dennyzhangd… www.cnblogs.com/dennyzhangd…