Spring 事务
什么是事务
事务:是数据库操作的最小单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行,要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)。
事务是四大特性
- 原子性(Atomicity):事务中所有操作是不可再分割的原子单元。事务中所有操作要么全部执行成功,要么全部执行失败。
- 一致性(Consistency):事务执行后,数据库状态与其他业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的连个账号余额之和应该是不变的。
- 隔离性(lsolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。
- 持久性(Durability):一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
事务的隔离级别
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted
、Read committed
、Repeatable read
、Serializable
。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读、事务丢失。
并发操作中出现的问题
脏读:(读取了未提交的新事物,然后被回滚了)
事务A读取了事务B中尚未提交的数据。如果事务B回滚,则A读取使用了错误的数据。不可重复读:(读取了提交的新事务,指更新操作)
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。幻读:(也是读取了提交的新事务,指增删操作)
在事务A多次读取构成中,事务B对数据进行了新增操作,导致事务A多次读取的数据不一致。第一类事物丢失:(称为回滚丢失)
对于第一类事物丢失,就是比如A和B同时在执行一个数据,然后B事物已经提交了,然后A事物回滚了,这样B事物的操作就因A事物回滚而丢失了。
第二类事物丢失:(提交覆盖丢失)
对于第二类事物丢失,也称为覆盖丢失,就是A和B一起执行一个数据,两个同时取到一个数据,然后B事物首先提交,但是A事物加下来又提交,这样就覆盖了B事物.
隔离级别
Read uncommitted
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。会产生脏读。Read committed
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。会产生不可重复读。Repeatable read
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。可能会产生幻读。Serializable
Serializable是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
Spring事务使用
基础配置,模拟一个转账业务
定义数据源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public DataSource dataSource(){
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/ssm?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}定义数据库操作dao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AccountDao {
private JdbcTemplate jdbcTemplate;
public void out(String fromName, Integer money){
String sql = "update account set money = money-? where name=?";
jdbcTemplate.update(sql, money, fromName);
}
public void in(String toName, Integer money){
String sql = "update account set money = money+? where name=?";
jdbcTemplate.update(sql, money, toName);
}
}定义service
1
2
3
4
5
6accountDao.out(fromName, money);
int x = 10;
if (x == 10){
throw new RuntimeException("Sql Error");
}
accountDao.in(toName, money);
编程式事务
添加事务管理器
1 | //事务管理器 |
使用编程式事务
1 |
|
代码块中的内容有一处失败,整体失败。
声明式事务
声明式事务不需要事务模板
配置类开启注解事务
1 |
|
添加事务管理器
1 | //事务管理器 |
添加事务
1 |
|
传播特性
物理事务和逻辑事务
物理事务:所谓物理事务指的就是Connection开启的事务。
逻辑事务:在一个复杂的业务系统中,可能会调用多个service,每个service都有自己的事务(标注了@Transactional
),此时我们需要根据事务传播方式(Propagation)来决定当前事务的行为(比如要挂起创建新事物,还是加入当前事务)。我们可以认为每个注解了@Transactional
的方法都是一个逻辑事务,这些逻辑事务被Spring事务管理,Spring会根据事务传播方式来决定是否开启新事务
7个传播特性
当一个事务方法被另外一个事务方法调用时,这个事务方法应该如何执行。
Spring的7种事务隔离级别
事务 | 描述 |
---|---|
PROPAGATION_REQUIRED | 默认事务类型,如果没有,就新建一个事务;如果有,就加入当前事务。适合绝大多数情况。 |
PROPAGATION_REQUIRES_NEW | 如果没有,就新建一个事务;如果有,就将当前事务挂起。 |
PROPAGATION_NESTED | 如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务(子事务回滚不影响父事务,但父事务回滚子事务需要回滚)。 |
PROPAGATION_SUPPORTS | 如果没有,就以非事务方式执行;如果有,就使用当前事务。 |
PROPAGATION_NOT_SUPPORTED | 如果没有,就以非事务方式执行;如果有,就将当前事务挂起即无论如何不支持事务。 |
PROPAGATION_NEVER | 如果没有,就以非事务方式执行;如果有,就抛出异常。 |
PROPAGATION_MANDATORY | 如果没有,就抛出异常;如果有,就使用当前事务。 |
- 不需要事务:
PROPAGATION_NEVER
,PROPAGATION_NOT_SUPPORTED
- 可有可无的:
PROPAGATION_SUPPORTS
- 必须要要有事务的:
PROPAGATION_REQUIRES_NEW
(总是新建),PROPAGATION_REQUIRED
(可新建,可加入),PROPAGATION_NESTED
(嵌套事务),PROPAGATION_MANDATORY
(抛出异常)
@EnableTransactionManagement注解
该注解导入了类TransactionManagementConfigurationSelector
,该类实现了ImportSelector
,.最终会执行selectImports
为容器注入bean。
1 | protected String[] selectImports(AdviceMode adviceMode) { |
这里会注入AutoProxyRegistrar
和ProxyTransactionManagementConfiguration
AutoProxyRegistrar
AutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
,回向容器注入需要的BeanDefiniton
,最终调用了AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)
,如果容器中没有名称为org.springframework.aop.config.internalAutoProxyCreator
的BeanDefinition
那么会注入InfrastructureAdvisorAutoProxyCreator
这里的InfrastructureAdvisorAutoProxyCreator
是是AbstractAutoProxyCreator
的子类,实现了SmartInstantiationAwareBeanPostProcessor
会在getEarlyBeanReference
提前暴露代理对象或者在bean完成实例化初始化后调用postProcessAfterInitialization
对原始的bean进行aop增强。也就是说这里会注入一个基于动态代理实现Aop增强的一个bean后置处理器
ProxyTransactionManagementConfiguration
类图:

注入
TransactionalEventListenerFactory
1
2
3
4
5
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
return new TransactionalEventListenerFactory();
}这一步是在其父类
AbstractTransactionManagementConfiguration
中完成的TransactionalEventListenerFactory
在事务的多播器中会把标注TransactionalEventListener
的方法包装成一个ApplicationListener
来响应对应的事件。注入
TransactionInterceptor
TransactionInterceptor
实现了MethodInterceptor
,是一个Advise
一个基于方法拦截器的通知,在其中实现了事务的增强逻辑。在Spring AOP
中我们分析过最后的aop代理对象执行方法的时候会调用到方法拦截器的invoke
方法,TransactionInterceptor
便是在其中实现了事务的增强。注入
BeanFactoryTransactionAttributeSourceAdvisor
BeanFactoryTransactionAttributeSourceAdvisor
是一个PointcutAdvisor
,是一个Advisor
,它使用Pointcut
来判断当前对象的方法是否需要增强,使用Advice
对方法进行增强。注入
TransactionAttributeSource
这里注入的是
AnnotationTransactionAttributeSource
,主要负责从类上,方法上,获取@Transactional
注解信息,比如抛出什么异常回滚,事务超时时间等。
我们基本上清楚了Spring 事务是如何实现的了,首先是通过BeanPostProcessor
与Spring IOC
容器结合在一起,在bean实例化初始化后调用后置处理器(如果出现循环依赖那么调用的是提前暴露对象的getEarlyBeanReference
)对bean进行Aop
增强,在AbstractAutoProxyCreator
的wrapIfNecessary
方法中,会获取全部的Advisor
,便会拿到注入的BeanFactoryTransactionAttributeSourceAdvisor
,然后使用Pointcut
进行过滤,然后通过ProxyFactory
选择使用JDK动态代理,还是cglib动态代理。后续调用代理对象的方法会调用到,TransactionInterceptor
中的invoke
实现了事务增强的逻辑。接下来我们详细看看细节部分
Spring事务源码分析
BeanFactoryTransactionAttributeSourceAdvisor
实现了PointcutAdvisor
接口,我们看下它的Pointcut
到底是什么,它的Advice
又是什么。
Pointcut
1 |
|
这个类维护了一个TransactionAttributeSourcePointcut
类型的pointcut

它实现了StaticMethodMatcherPointcut
,它是一个静态方法匹配的Pointcut,这里的静态意思是不会因为入参的参数不同而改变过滤结果。我们看下具体的实现逻辑。
1 |
|
这里的getTransactionAttributeSource
返回的是AnnotationTransactionAttributeSource
实例,这里的逻辑是只要有TransactionAttributeSource
并且可以拿到事务定义信息,那么就视为匹配,后面就会对方法进行增强。
AnnotationTransactionAttributeSource是如何获取事务定义信息的
getTransactionAttribute
由其父类AbstractFallbackTransactionAttributeSource
实现,其使用一个ConcurrentHashMap
缓存方法和其对应的事务信息,如果缓存中没有那么会调用computeTransactionAttribute
方法进行获取。computeTransactionAttribute
方法会优先获取方法上面的事务注解信息,然后获取类类上面的注解信息。
1 |
|
computeTransactionAttribute
的方法
1 |
|
可以看到事务注解只能标注在public方法上面,如果是像mybatis这种生成接口动态代理类那么会拿到接口上面的注解信息。
获取注解信息findTransactionAttribute
调用了子类的AnnotationTransactionAttributeSource#determineTransactionAttribute
方法
1 |
|
这里涉及到一个新的类TransactionAnnotationParser
,这是Spring根据AnnotatedElement
获取注解的接口,可以扩展此接口实现自己的事务注解,并定制事务定义信息。这些事务注解解析器在构造方法中进行了定义。
1 | public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { |
可以看到Spring内置了JTA
,ejb
的事务注解处理器,但是使用的是LinkedHashSet
,SpringTransactionAnnotationParser
会放在最前面,解析Spring的@Transactional
注解,这也是我们最常用的事务注解,SpringTransactionAnnotationParser
会拿到事务注解信息,然后把注解的属性内容包装成 RuleBasedTransactionAttribute
RuleBasedTransactionAttribute
对rollbackOn
进行了扩展,配合SpringTransactionAnnotationParser
会解析事务注解中的rollbackFor
,rollbackForClassName
,noRollbackFor
,noRollbackForClassName
属性来判断异常抛出时是否需要回滚事务。如果这个属性没有值的话,会调用父类的rollbackOn
,最终只会在RuntimeException
和 Error
上面回滚。
BeanFactoryTransactionAttributeSourceAdvisor是如何获取到Advice
的
它实现了BeanFactoryAware
,使用advice
持有当前通知的引用,如果没有那么从容器中根据名称拿。在ProxyTransactionManagementConfiguration
中直接设置advice
为容器中的TransactionInterceptor
,也就是说事务的增强逻辑定义在TransactionInterceptor
中。
1 | //AbstractBeanFactoryPointcutAdvisor |
TransactionInterceptor
TransactionInterceptor
是事务拦截器,其invoke方法会在代理对象被代理方法执行的时候被回调到
1 |
|
其中invokeWithinTransaction
是spring事务原理的关键,此方法在其父类TransactionAspectSupport
实现
利用TransactionAttributeSource获取方法或者类上的事务注解信息
1 | //invokeWithinTransaction |
事务注解决定了事务增强的代码执行逻辑,这里使用的TransactionAttributeSource
通常是是上文中我们提到的AnnotationTransactionAttributeSource
,上文中我们直到,其内部存在map缓存,如果缓存没有那么调用TransactionAnnotationParser
链式的进行解析,自然就调用到了SpringTransactionAnnotationParser
,SpringTransactionAnnotationParser
反射获取注解信息包装成TransactionAttribute
对象
获取事务管理器PlatformTransactionManager
1 | //invokeWithinTransaction |
PlatformTransactionManager
是spring抽象出的事务管理器接口,主要包含下面三个方法
1 | public interface PlatformTransactionManager extends TransactionManager { |
determineTransactionManager
方法决定使用什么事务管理器
1 | // invokeWithinTransaction |
这里可以看到Spring是支持@Transactional
注解value指定特定的事务管理器,但是我们实际使用中通常是没有指定的,这里就会去bean工程中获取PlatformTransactionManager
类型的bean,存在多个就会抛出异常了。这里拿到的一般是DataSourceTransactionManager
根据事务传播级别来决定是否由必要创建事务createTransactionIfNecessary
1 | // invokeWithinTransaction |
首先调用getTransaction
方法获取一个事务,然后调用prepareTransactionInfo
封装事务的处理配置信息并绑定到当前线程
1 | protected TransactionInfo createTransactionIfNecessary( PlatformTransactionManager tm, |
其中getTransaction
在AbstractPlatformTransactionManager
中是一个final
的模板方法。
1 | // getTransaction |
不存在事务
首先要求超时时长不能小于-1.-1表示的使用底层事务系统的默认超时,如果不支持超时,则使用无
1
2
3if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}然后如果的传播级别是
MANDATORY
支持当前事务,如果不存在则抛出异常,也就是说MANDATORY
要求外层调用方法是在一个具备事务的情况下进行的调用.1
2
3
4if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}如果隔离级别是
Required(支持当前事务,如果当前不存在事务那么创建一个新事务)
,RequireNew(创建一个新事务,如果存在事务则暂停当前事务)
,Nested(如果当前事务存在,则在嵌套事务中执行)
那么会执行下面的逻辑1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 挂起当前事务,让事务和线程解绑
SuspendedResourcesHolder suspendedResources = suspend(null);
// log
try {
// 开启一个事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
// 出现异常恢复被挂起的事务
resume(null, suspendedResources);
throw ex;
}
}
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启具体事务
doBegin(transaction, definition);
// 设置当前事务的一些属性记录在ThreadLocal中
prepareSynchronization(status, definition);
return status;
}首先是挂起当前事务,由于当前不存在事务,其实是把当前存在
TransactionSynchronization
事务同步回调的接口信息保存在SuspendedResourcesHolder
中。在DataSourceTransactionManager
中doBegin
方法会获取对应的Connection,然后根据事务定义对Connection进行设置,比如如果是只读事务那么会执行SET TRANSACTION READ ONLY
设置事务只读,设置超时时长,关闭自动提交等,并且把创建的事务信息绑定到resources ThreadLocal
中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
public static void initSynchronization() throws IllegalStateException {
if (isSynchronizationActive()) {
throw new IllegalStateException("Cannot activate transaction synchronization - already active");
}
logger.trace("Initializing transaction synchronization");
synchronizations.set(new LinkedHashSet<>());
}prepareSynchronization
负责把事务相关信息设置到ThreadLocal中,并且初始化TransactionSynchronization
的LinkedHashSet
,这样我们我们通过TransactionSynchronizationManager
加入一些回调方法的时候不会抛NPE,之所以使用LinkedHashSet
是TransactionSynchronizationManager
支持Ordered接口
,@Order注解
进行排序如果是
SUPPORTS(支持当前事务,如果不存在,则以非事务方式执行)
,NOT_SUPPORTED(不支持当前事务;始终以非事务方式执行)
,NEVER(不支持当前事务,如果当前事务存在,则抛出异常)
那么不会开启事务,但是支持事务同步1
2
3
4
5
6
7
8else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
// log
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
存在事务的情况
是如何判断是否存在事务的首先
doGetTransaction
方法会获取resources ThreadLocal
中的事务信息,这个事务信息是在doBegin
方法中绑定1
2
3
4
5
6
7
8protected Object doGetTransaction() {
DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();
txObject.setSavepointAllowed(this.isNestedTransactionAllowed());
// 从threadLocal中获取事务资源
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}然后调用
isExistingTransaction
来判断是否存在事务1
2
3
4protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
return txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive();
}同样
doBegin
方法会设置transactionActive
为true。
最后如果存在事务那么会执行下面的handleExistingTransaction
来根据事务传播级别来创建事务首先如果传播级别是
NEVER(不支持当前事务,如果当前事务存在,则抛出异常)
当前存在事务,那么抛出异常1
2
3
4if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}如果传播级别是
NOT_SUPPORTED(不支持当前事务;始终以非事务方式执行)
那么不会开启事务,但是支持事务同步,并且会挂起当前事务,其中prepareTransactionStatus
会初始化事务同步set,并且把当前事务的信息包装到DefaultTransactionStatus
并返回1
2
3
4
5
6
7
8if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
// log
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 会初始化事务同步的set集合
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}如果传播级别是
RequireNew(创建一个新事务,如果存在事务则暂停当前事务)
会挂起当前事务,从ThreadLocal
中解绑,然后开启新事务,并初始化TransactionSynchronization
的set1
2
3
4
5
6
7
8
9
10
11
12
13
14if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// log
// 挂起事务
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 开启事务
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
// 将之前的事务恢复到ThreadLocal
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}如果传播级别是
Nested(如果当前事务存在,则在嵌套事务中执行)
,首先会判断是否允许嵌套事务,如果不允许那么抛出异常,通常DataSourceTransactionManager
使用savepoint
来实现嵌套事务1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 判断是否允许嵌套事务
if (!isNestedTransactionAllowed()) {
// 抛出异常
}
// ..log
// 使用savepoint来执行嵌套事务
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 {
// 这里是JTA使用XA实现分布式事务的逻辑
// 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);
}
}调用
Connection#setSavepoint
方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// JdbcTransactionObjectSupport
public Object createSavepoint() throws TransactionException {
ConnectionHolder conHolder = this.getConnectionHolderForSavepoint();
try {
if (!conHolder.supportsSavepoints()) {
throw new NestedTransactionNotSupportedException("Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
} else if (conHolder.isRollbackOnly()) {
throw new CannotCreateTransactionException("Cannot create savepoint for transaction which is already marked as rollback-only");
} else {
return conHolder.createSavepoint();
}
} catch (SQLException var3) {
throw new CannotCreateTransactionException("Could not create JDBC savepoint", var3);
}
}
// ConnectionHolder
public Savepoint createSavepoint() throws SQLException {
++this.savepointCounter;
return this.getConnection().setSavepoint("SAVEPOINT_" + this.savepointCounter);
}最后如果是
Required(支持当前事务,如果当前不存在事务那么创建一个新事务) SUPPORTS(支持当前事务;如果不存在,则以非事务方式执行) MANDATORY(支持当前事务;如果当前不存在事务,则抛出异常)
1
2boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);都不会产生新的事务。
prepareTransactionInfo包装事务信息并绑定到ThreadLocal
1 | // createTransactionIfNecessary |
这里会使用oldTransactionInfo
记录之前的事务信息,并且绑定当前事务信息到ThreadLocal
上,这样A事务方法调用B事务方法的时,能像栈一样先进后出。在invokeWithinTransaction
调用完业务方法后,会调用cleanupTransactionInfo
把oldTransactionInfo
重写设置到ThreadLocal
中,这意味着B方法执行结束,回到了A方法的调用栈中。
回调业务逻辑InvocationCallback#proceedWithInvocation
1 | // invokeWithinTransaction |
其实调用的是MethodInvocation#proceed
,如果当前对象存在多层代理,比如先事务代理,再基于@AspectJ
注解的方法调用时长统计,那么后面代理增强也会执行,具体逻辑在ReflectiveMethodInvocation#proceed
方法中,如果还存在其他的拦截器链那么会继续执行拦截器中的逻辑,否则直接执行我们自己的业务逻辑代码。
completeTransactionAfterThrowing 业务逻辑出现异常时的处理
1 | // invokeWithinTransaction |
这里rollbackOn
取决于事务注解上面标注的在什么异常上回滚,在什么异常上不回滚,默认是在RuntimeException
和Error
上面才会回滚。
事务是如何回滚的
1 |
|
回滚策略
1 | private void processRollback(DefaultTransactionStatus status, boolean unexpected) { |
执行回滚DataSourceTransactionManager
是调用的Connection#rollback
方法,这里首先会获取ThreadLocal
中的TransactionSynchronization
并且按照@Order
和Ordered
排序然后依次调用beforeCompletion
,如果具备回滚点,那么直接回滚到保存点,如果是一个新事务,那么直接回滚,如果不是一个独立的事务,只是标记需要回滚,执行完这些后,还会回调triggerAfterCompletion
,然后调用TransactionSynchronization#afterCompletion
方法,然后调用cleanupAfterCompletion
清理资源,并且恢复被挂起的线程。
commitTransactionAfterReturning提交事务
1 | // invokeWithinTransaction |
具体逻辑在processCommit
中进行
1 | // processCommit |
DataSourceTransactionManager
提交事务是执行的是Connection#commit
同样完成只会还会调用triggerAfterCommit
,triggerAfterCompletion
并清理ThreadLocal中的内容,并且恢复被挂起的事务。
事务同步回调接口TransactionSynchronization

其中beforeCommit
,beforeCompletion
,aterCommit
,afterCompletion
,只有当前事务是一个独立事务的时候才会回调,而且如果提交或者回滚的时候出现异常,beforeCompletion
,afterCompletion
也会被调用,我们可以通过TransactionSynchronizationManager#registerSynchronization
注册回调的逻辑。

那么什么时候事务被视作时一个独立事务昵
- 如果外部不存在一个事务,并且传播级别是
REQUIRED,REQUIRES_NEW,NESTED
- 如果外部存在一个事务,且传播级别为
REQUIRES_NEW
- 如果外部存在一个事务,且传播级别为嵌套事务,但是此时不是通过保存点来实现嵌套事务
事务失效的场景
方法内的自调用: Spring事务是基于AOP的, 只要使用代理对象调用某个方法时,Spring事务才能生效, 而在一个方法中调用使用
this.xx()
调用方法时,this并不是代理目对象,所以会导致事务失效。导致的方法上的注解@Transcational
失效。- 解放办法1:把调用方法拆分到另外-个Bean中
- 解决办法2:自己注入自己
- 解决办法3:
AopContext.currentProxy()
+@EnableAspectUAutoProxy(exposeProxy=true)
方法是
private
的: Spring事务 spring事务的实现AbstractFallbackTransactionAttributeSource
类的computeTransactionAttribute
方法中有个判断,如果目标方法不是public,则TransactionAttribute
返回null,即不支持事务。方法是final的:原因和private是一样的,因为spring事务底层实现使用了代理,aop,通过jdk的动态代理或者cglib,生成了代理类,在代理类中实现了事务功能,如果方法被final修饰,无法重写该方法,也就无法添加事务的功能了
多个的线程调用方法:当
Mybatis
或JdbcTemplate
执行SQL时, 会从ThreadLocal
中去获取数据库连接对象, 如果开启事务的线程和执行SQL的线程是同一个,那么就能
拿到数据库连接对象,如果不是同一个线程,那就拿到不到数据库连接对象,这样,Mybatis或ldbcTemplate
就会自 己去新建一个数据库连接用来执行SQL, 此数据库连
接的autocommit
为true,那么执行完SQL就会提交,后续再抛异常也就不能再回滚之前已经提交了的SQL了。没加@Configuration注解:如果用
SpringBoot
基本没有这个问题, 但是如果用的Spring, 那么可能会有这个问题,这个问题的原因其实也是由于Mybatis
或JdbcTemplate
会从ThreadLocal
中去获取数据库连接,但是ThreadLocal
中存储的是一 个MAP, MAP的key为DataSource
对象, value为连接对象, 而如果我们没有在AppConfig
上添加@Configuration
注解的话, 会导致MAP中存的DataSource
对象和Mybatis
和JdbcTemplate
中的DataSource
对象不相等, 从而也拿不到数据库连接,导致自己去创建数据库连接了。异常被吃掉:如果Spring事务没有捕获到异常,那么也就不会回滚了,默认情况下Spring会捕获
RuntimeException
和Error
。类没有被Spring管理
数据库不支持事务
参考