需求
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种事务传播机制,很多人对传播机制的曲解从概念开始,所以特地翻译了一下源码注释如下:
-
PROPAGATION_REQUIRED 支持当前事务;如果不存在,创建一个新的。类似于同名的EJB事务属性。这通常是事务定义的默认设置,通常定义事务同步作用域。
-
PROPAGATION_SUPPORTS 支持当前事务;如果不存在事务,则以非事务方式执行。类似于同名的EJB事务属性。 注意:
对于具有事务同步的事务管理器,PROPAGATION_SUPPORTS与没有事务稍有不同,因为它可能在事务范围内定义了同步。因此,相同的资源(JDBC的Connection、Hibernate的Session等)将在整个指定范围内共享。注意,确切的行为取决于事务管理器的实际同步配置!
小心使用PROPAGATION_SUPPORTS!特别是,不要依赖PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW,在PROPAGATION_SUPPORTS范围内(这可能导致运行时的同步冲突)。如果这种嵌套不可避免,请确保适当地配置事务管理器(通常切换到“实际事务上的同步”)。
-
PROPAGATION_MANDATORY 支持当前事务;如果当前事务不存在,抛出异常。类似于同名的EJB事务属性。 注意: PROPAGATION_MANDATORY范围内的事务同步总是由周围的事务驱动。
-
PROPAGATION_REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起当前事务。类似于同名的EJB事务属性。
注意:实际事务挂起不会在所有事务管理器上开箱即用。这一点特别适用于JtaTransactionManager,它需要TransactionManager的支持。
PROPAGATION_REQUIRES_NEW范围总是定义自己的事务同步。现有同步将被挂起并适当地恢复。
- PROPAGATION_NOT_SUPPORTED 不支持当前事务,存在事务挂起当前事务;始终以非事务方式执行。类似于同名的EJB事务属性。
注意:实际事务挂起不会在所有事务管理器上开箱即用。这一点特别适用于JtaTransactionManager,它需要TransactionManager的支持。
事务同步在PROPAGATION_NOT_SUPPORTED范围内是不可用的。现有同步将被挂起并适当地恢复。
- PROPAGATION_NEVER 不支持当前事务;如果当前事务存在,抛出异常。类似于同名的EJB事务属性。
注意:事务同步在PROPAGATION_NEVER范围内不可用。
- PROPAGATION_NESTED 如果当前事务存在,则在嵌套事务中执行,如果当前没有事务,类似PROPAGATION_REQUIRED(创建一个新的)。EJB中没有类似的功能。
注意:实际创建嵌套事务只对特定的事务管理器有效。开箱即用,这只适用于 DataSourceTransactionManager(JDBC 3.0驱动)。一些JTA提供者也可能支持嵌套事务。
下面是隔离级别的表格:
注意: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()方法中。
- 从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
- 模板方法创建事务,并根据事务传播属性开启事务 调用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持有者到当前线程
}
- 通过反射真正执行@Transactional注解方法中的代码。 根据事务定义的,该异常需要回滚就回滚,否则提交事务;若未异常则正常提交返回
retVal = invocation.proceedWithInvocation();
commit
- 入口: 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);
}
- 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
- 入口: 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);
}
- 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