Appearance
Spring事务详解
要理解Spring事务的原理,我们需要从事务的本质、Spring的事务抽象、实现机制(AOP、线程绑定)、核心组件(事务管理器、事务定义、事务状态)、事务属性(传播行为、隔离级别)等多个维度逐步拆解。以下是系统的讲解:
一、事务的基础:ACID特性
事务(Transaction)是一组原子性的操作集合,要么全部成功,要么全部失败。它的核心是保证数据的一致性(Consistency),而这依赖于四个关键特性(ACID):
- 原子性(Atomicity):操作不可分割,要么全做,要么全不做;
- 一致性(Consistency):事务执行前后,数据从一个合法状态转移到另一个合法状态(如转账后双方余额之和不变);
- 隔离性(Isolation):多个事务并发执行时,彼此之间互不干扰;
- 持久性(Durability):事务提交后,数据的修改永久保存(即使系统崩溃也不丢失)。
数据库(如MySQL InnoDB)本身提供了事务支持,但不同持久层框架(JDBC、Hibernate、MyBatis)的事务API各不相同。Spring事务的核心价值是:统一事务编程模型,屏蔽底层框架差异,让开发者无需关心具体的事务实现细节。
二、Spring事务的核心组件
Spring通过三个核心接口抽象了事务的整个生命周期,这是理解Spring事务的关键:
1. PlatformTransactionManager:事务管理器(核心)
PlatformTransactionManager是Spring事务的核心接口,定义了事务的基本操作(获取、提交、回滚)。它是策略模式的体现——不同的持久层框架对应不同的实现类:
| 持久层框架 | 对应的PlatformTransactionManager实现类 |
|---|---|
| JDBC/MyBatis | DataSourceTransactionManager |
| Hibernate | HibernateTransactionManager |
| JPA | JpaTransactionManager |
| Redis(非关系型) | RedisTransactionManager(Spring Data Redis) |
接口定义:
java
public interface PlatformTransactionManager {
// 获取事务(根据TransactionDefinition)
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交事务
void commit(TransactionStatus status) throws TransactionException;
// 回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}2. TransactionDefinition:事务属性定义
TransactionDefinition定义了事务的属性配置,包括:
- 传播行为(Propagation):解决“事务方法调用事务方法”时的事务归属问题(最核心的属性);
- 隔离级别(Isolation):解决并发事务的干扰问题;
- 超时时间(Timeout):事务的最大执行时间(防止长事务占用资源);
- 只读属性(Read-Only):标记事务为只读(数据库可优化,如避免写锁)。
接口定义(关键方法):
java
public interface TransactionDefinition {
// 传播行为(默认REQUIRED)
int getPropagationBehavior();
// 隔离级别(默认DEFAULT,即数据库默认)
int getIsolationLevel();
// 超时时间(默认-1,无超时)
int getTimeout();
// 是否只读(默认false)
boolean isReadOnly();
}3. TransactionStatus:事务状态
TransactionStatus代表当前事务的运行状态(如是否新事务、是否有保存点、是否已回滚),是事务执行过程中的“状态快照”。Spring通过它来控制事务的生命周期(如提交、回滚)。
接口定义(关键方法):
java
public interface TransactionStatus extends SavepointManager {
// 是否是新事务(非嵌套、非加入已有事务)
boolean isNewTransaction();
// 是否有保存点(用于嵌套事务)
boolean hasSavepoint();
// 设置事务为“仅回滚”(手动触发回滚)
void setRollbackOnly();
// 是否已标记为回滚
boolean isRollbackOnly();
// 事务是否已完成(提交或回滚)
boolean isCompleted();
}三、Spring事务的实现机制
Spring事务分为编程式事务和声明式事务,其中声明式事务(基于@Transactional注解)是最常用的方式,底层依赖AOP和线程绑定。
1. 编程式事务:显式控制事务
编程式事务需要手动调用事务管理器的API(如TransactionTemplate),代码侵入性强,但灵活。
示例:使用TransactionTemplate:
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private TransactionTemplate transactionTemplate;
public void createUser(User user) {
// 执行事务逻辑:若抛出异常则回滚
transactionTemplate.execute(status -> {
userMapper.insert(user);
if (user.getName().equals("admin")) {
// 手动标记回滚
status.setRollbackOnly();
}
return null;
});
}
}原理:TransactionTemplate封装了PlatformTransactionManager的操作,execute方法内部会:
- 调用
getTransaction获取事务; - 执行用户的业务逻辑;
- 根据业务逻辑的执行结果(正常/异常)调用
commit或rollback。
2. 声明式事务:基于AOP的无侵入式事务
声明式事务通过@Transactional注解标记需要事务的方法/类,Spring通过AOP动态代理自动为目标方法添加事务逻辑(开启、提交、回滚)。这是Spring事务的核心优势——无代码侵入。
(1)声明式事务的核心原理:AOP拦截
Spring会为带有@Transactional的Bean创建代理对象(JDK动态代理或CGLIB代理)。当调用代理对象的方法时,会先执行事务拦截器(TransactionInterceptor)的逻辑,再执行目标方法。
事务拦截器的执行流程(关键步骤):
mermaid
sequenceDiagram
participant Client
participant Proxy(代理对象)
participant TransactionInterceptor(事务拦截器)
participant Target(目标对象)
participant PlatformTransactionManager(事务管理器)
Client->>Proxy: 调用方法(如createUser)
Proxy->>TransactionInterceptor: 触发拦截
TransactionInterceptor->>PlatformTransactionManager: getTransaction(TransactionDefinition) ① 获取事务
PlatformTransactionManager->>TransactionInterceptor: 返回TransactionStatus
TransactionInterceptor->>Target: 执行目标方法(如userMapper.insert) ② 执行业务
alt 目标方法正常返回
TransactionInterceptor->>PlatformTransactionManager: commit(TransactionStatus) ③ 提交事务
else 目标方法抛出异常
TransactionInterceptor->>PlatformTransactionManager: rollback(TransactionStatus) ③ 回滚事务
end
TransactionInterceptor->>Proxy: 返回结果
Proxy->>Client: 返回结果(2)事务拦截器的核心逻辑(源码简化)
TransactionInterceptor是MethodInterceptor的实现类,其invoke方法是事务逻辑的入口。以下是简化后的核心代码:
java
public class TransactionInterceptor implements MethodInterceptor {
private PlatformTransactionManager transactionManager;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 1. 获取事务属性(@Transactional的配置)
TransactionAttribute txAttr = getTransactionAttribute(invocation);
// 2. 获取事务管理器(如DataSourceTransactionManager)
PlatformTransactionManager tm = getTransactionManager(txAttr);
// 3. 获取目标方法的唯一标识(用于日志等)
String joinpointIdentification = methodIdentification(invocation.getMethod(), invocation.getThis().getClass());
try {
// 4. 开启事务:获取TransactionStatus
TransactionStatus status = tm.getTransaction(txAttr);
try {
// 5. 执行目标方法(业务逻辑)
Object result = invocation.proceed();
// 6. 提交事务
tm.commit(status);
return result;
} catch (Throwable ex) {
// 7. 回滚事务(根据异常类型判断是否回滚)
tm.rollback(status);
throw ex;
}
} finally {
// 清理资源
}
}
}3. 线程绑定:事务资源的共享
Spring事务的关键实现细节是线程绑定——通过TransactionSynchronizationManager(事务同步管理器)将事务资源(如JDBC的Connection、Hibernate的Session)绑定到当前线程的ThreadLocal变量中。这样,同一线程内的所有操作(如多个Mapper调用)都会共享同一个事务资源,从而保证事务的原子性。
(1)TransactionSynchronizationManager的核心作用
TransactionSynchronizationManager是一个工具类,内部维护了多个ThreadLocal变量:
resources:存储当前线程的事务资源(键是资源工厂,如DataSource;值是资源,如Connection);transactionSynchronizations:存储当前线程的事务同步回调(如TransactionSynchronization);currentTransactionName:当前事务的名称;currentTransactionReadOnly:当前事务是否只读;currentTransactionIsolationLevel:当前事务的隔离级别。
示例:JDBC事务的线程绑定流程 当使用DataSourceTransactionManager时,事务的开启过程会:
- 从
DataSource获取一个Connection; - 调用
connection.setAutoCommit(false)(关闭自动提交); - 将
Connection绑定到TransactionSynchronizationManager的resources中; - 后续的JDBC操作(如
JdbcTemplate、MyBatis)会从resources中获取该Connection,确保同一事务内的操作使用同一个Connection。
关键代码(DataSourceTransactionManager的doBegin方法):
java
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 1. 获取Connection(从DataSource)
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 2. 关闭自动提交
con = txObject.getConnectionHolder().getConnection();
con.setAutoCommit(false);
// 3. 设置隔离级别(如果不是默认)
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
con.setTransactionIsolation(definition.getIsolationLevel());
}
// 4. 绑定Connection到当前线程
TransactionSynchronizationManager.bindResource(this.dataSource, txObject.getConnectionHolder());
} catch (SQLException ex) {
// 异常处理
}
}四、Spring事务的核心属性详解
@Transactional注解的配置决定了事务的行为,以下是最核心的两个属性:
1. 事务传播行为(Propagation)
传播行为解决的是**“一个事务方法调用另一个事务方法时,事务如何传递”的问题。Spring定义了7种传播行为(Propagation枚举),最常用的是REQUIRED和REQUIRES_NEW**。
| 传播行为 | 含义 | 场景示例 |
|---|---|---|
| REQUIRED(默认) | 当前有事务则加入,没有则新建。 | 大多数业务场景(如“创建订单+扣减库存”,两个操作必须在同一事务中) |
| REQUIRES_NEW | 无论当前是否有事务,都新建一个独立事务(原事务挂起)。 | 日志记录(即使主事务回滚,日志也需要保留) |
| SUPPORTS | 当前有事务则加入,没有则无事务执行。 | 查询操作(可选事务) |
| MANDATORY | 当前必须有事务,否则抛出异常。 | 核心业务逻辑(必须在事务中执行) |
| NOT_SUPPORTED | 以无事务方式执行,若当前有事务则挂起。 | 长耗时操作(如导出Excel,避免占用事务资源) |
| NEVER | 不允许在事务中执行,否则抛出异常。 | 纯查询操作(确保不会修改数据) |
| NESTED | 嵌套事务(基于保存点Savepoint),若当前有事务则在其内部创建子事务。 | 部分回滚场景(如“下单”失败时,只回滚“扣减库存”,不回滚“生成订单记录”) |
示例:传播行为的差异 假设ServiceA.methodA()调用ServiceB.methodB():
- 若
methodA的传播行为是REQUIRED,methodB的传播行为是REQUIRED:同一事务(methodB加入methodA的事务); - 若
methodA的传播行为是REQUIRED,methodB的传播行为是REQUIRES_NEW:两个独立事务(methodA的事务挂起,methodB执行完后恢复methodA的事务)。
2. 事务隔离级别(Isolation)
隔离级别解决的是**“并发事务之间的干扰”**问题。数据库的并发操作会导致三类问题:
- 脏读(Dirty Read):读取到未提交的修改;
- 不可重复读(Non-Repeatable Read):同一事务内多次读取同一数据,结果不一致(被其他事务修改并提交);
- 幻读(Phantom Read):同一事务内多次查询同一条件,结果集数量不一致(被其他事务插入/删除)。
Spring的隔离级别对应数据库的隔离级别(Isolation枚举),默认是DEFAULT(使用数据库的默认隔离级别,如MySQL InnoDB默认是可重复读(REPEATABLE READ))。
| 隔离级别 | 解决的问题 | 数据库支持情况 |
|---|---|---|
| DEFAULT(默认) | 依赖数据库 | 所有数据库 |
| READ_UNCOMMITTED | 无(允许脏读) | MySQL、PostgreSQL等 |
| READ_COMMITTED | 脏读 | 所有主流数据库 |
| REPEATABLE_READ | 脏读、不可重复读 | MySQL InnoDB、PostgreSQL等 |
| SERIALIZABLE | 所有问题(串行执行) | 所有数据库(性能最低) |
注意:隔离级别越高,并发性能越低。需根据业务场景权衡(如金融系统用SERIALIZABLE,普通系统用READ_COMMITTED)。
3. 其他属性
timeout:事务的最大执行时间(单位:秒)。若超过时间,Spring会强制回滚事务(防止长事务占用数据库连接);readOnly:标记事务为只读。数据库会优化这类事务(如MySQL InnoDB会避免写锁,Hibernate会关闭一级缓存的刷新);rollbackFor/noRollbackFor:控制事务回滚的异常类型。默认情况下,Spring只回滚RuntimeException和Error(检查型异常如IOException不会回滚)。如需回滚检查型异常,需设置rollbackFor = Exception.class。
五、Spring事务的常见问题与解决方案
1. 事务不生效的常见原因
- (1)方法不是
public:Spring AOP默认只拦截public方法(@Transactional对非public方法无效); - (2)类未被Spring管理:未加
@Component/@Service等注解,导致Spring无法创建代理对象; - (3)内部方法调用:同一类中的方法A调用方法B(
B有@Transactional),此时是内部调用,不会经过代理对象,事务不生效。解决方案:① 将方法B放到另一个类中;② 注入自身的代理对象(@Autowired自身,或使用AopContext.currentProxy()); - (4)异常被捕获且未抛出:方法内部
try-catch了异常但未重新抛出,Spring无法感知异常,不会回滚。解决方案:catch后重新抛出异常(如throw new RuntimeException(e)); - (5)未开启事务管理:未加
@EnableTransactionManagement注解(Spring Boot自动开启,但非Boot项目需手动加); - (6)数据源与事务管理器不匹配:如使用
MyBatis但配置了HibernateTransactionManager,导致事务无法管理MyBatis的SqlSession; - (7)多线程调用:事务是线程绑定的,子线程中的操作不会加入父线程的事务。解决方案:手动传递事务上下文,或使用分布式事务(如Seata)。
2. 长事务的问题与优化
长事务(如事务中包含远程调用、文件上传、用户等待)会导致:
- 数据库连接池耗尽(连接被长时间占用);
- 锁竞争加剧(如InnoDB的行锁不会释放);
- 事务超时风险增加。
优化方案:
- 缩小事务边界:将非核心操作(如日志、缓存更新)放到事务外(可通过
TransactionSynchronization在事务提交后执行); - 异步处理:将长耗时操作放到异步线程中(如
@Async); - 拆分事务:将大事务拆分为多个小事务(需保证最终一致性)。
六、Spring事务与分布式事务
Spring的本地事务(上文讲解的)仅能解决单数据源的事务问题。对于多数据源或分布式系统(如微服务),需要使用分布式事务解决方案。
1. 分布式事务的挑战
分布式系统中,事务涉及多个服务/数据源,无法通过本地事务保证ACID。例如:
- 用户下单(服务A,数据库A)→ 扣减库存(服务B,数据库B)→ 记录日志(服务C,数据库C)。若服务B失败,服务A和C的操作需要回滚,但本地事务无法跨服务/数据源。
2. Spring的分布式事务支持
Spring本身没有实现分布式事务,但整合了主流的分布式事务框架:
- XA事务(两阶段提交,2PC):通过
JtaTransactionManager整合支持XA的数据库/中间件(如Atomikos、Bitronix); - TCC(Try-Confirm-Cancel):通过
Spring Cloud TCC或Seata实现; - Saga:通过
Seata或Apache ServiceComb实现; - 最终一致性:通过消息队列(如RocketMQ)实现(事务消息)。
示例:Seata的分布式事务 Seata是阿里开源的分布式事务框架,支持XA、TCC、Saga等模式。Spring Boot整合Seata只需:
- 引入Seata依赖;
- 配置Seata的注册中心(如Nacos);
- 在需要分布式事务的方法上添加
@GlobalTransactional注解(替代@Transactional)。
七、总结
Spring事务的核心是**“统一抽象+AOP+线程绑定”**:
- 统一抽象:通过
PlatformTransactionManager屏蔽底层框架差异; - AOP:通过动态代理实现声明式事务,无代码侵入;
- 线程绑定:通过
TransactionSynchronizationManager将事务资源绑定到当前线程,保证同一事务内的操作共享资源。
理解Spring事务的关键是掌握核心组件的职责(事务管理器、事务定义、事务状态)和事务属性的作用(传播行为、隔离级别)。在实际开发中,需注意事务的边界、异常处理、多线程场景,避免事务不生效或长事务问题。
扩展阅读:
- Spring官方文档:Transaction Management
- Seata官方文档:Seata Distributed Transaction
- MySQL InnoDB事务:InnoDB Transaction Model
