🧾 一、什么是事务?
🧠 通俗理解:
事务 = 一组操作,要么全部成功,要么全部失败,不能只做一半。
比如你转账:
- A 账户扣钱
- B 账户加钱
如果 A 扣了钱但 B 没收到,那就出问题了!这就是 数据不一致,事务就是为了解决这类问题。
✅ 二、事务的四大特性(ACID)
| 特性 | 解释 | 
|---|---|
| 原子性(Atomicity) | 要么全部成功,要么全部失败 | 
| 一致性(Consistency) | 操作前后数据处于一致状态 | 
| 隔离性(Isolation) | 多个事务互不干扰 | 
| 持久性(Durability) | 提交后的数据是永久保存的 | 
💡 三、Spring 中的事务管理方式
Spring 支持两种事务管理方式:
1. 编程式事务管理(不常用)
手动写事务边界,代码控制事务提交/回滚。
TransactionStatus status = txManager.getTransaction(...);
try {// 业务代码txManager.commit(status);
} catch (Exception e) {txManager.rollback(status);
}
🚫 缺点:代码侵入性强、重复、易出错。实际开发中不推荐。
2. 声明式事务管理 ✅(主流)
通过注解或配置来管理事务,干净、简洁、优雅!
@Service
public class AccountService {@Transactionalpublic void transfer() {// 扣钱// 加钱// 如果中间出异常,自动回滚}
}
📌 四、@Transactional 注解详解
@Transactional 是 Spring 中声明事务的注解,作用范围可以是类或方法。
常用属性:
| 属性 | 含义 | 
|---|---|
| propagation | 事务传播行为(默认 REQUIRED) | 
| isolation | 事务隔离级别 | 
| rollbackFor | 指定哪些异常触发回滚(默认是运行时异常) | 
| readOnly | 是否只读事务 | 
| timeout | 设置事务超时时间 | 
🔄 五、事务的传播行为(重点)
| 行为 | 含义 | 
|---|---|
| REQUIRED(默认) | 有事务就加入,没有就新建 | 
| REQUIRES_NEW | 每次都新建一个事务,原事务挂起 | 
| NESTED | 嵌套事务,有独立的回滚点 | 
| SUPPORTS | 有事务就用,没有就不用 | 
| NOT_SUPPORTED | 不支持事务,挂起当前事务 | 
| NEVER | 当前不能存在事务,否则抛异常 | 
| MANDATORY | 必须存在事务,否则抛异常 | 
🔒 六、事务隔离级别(对应数据库的)
| 隔离级别 | 解决的问题 | 说明 | 
|---|---|---|
| DEFAULT | 使用数据库默认 | |
| READ_UNCOMMITTED | 脏读 | 最低,性能高但不安全 | 
| READ_COMMITTED | 不可重复读 | 常用,如 Oracle 默认 | 
| REPEATABLE_READ | 幻读 | MySQL 默认 | 
| SERIALIZABLE | 最强隔离 | 安全但性能差 | 
💣 七、事务生效的注意事项(易踩坑)
🚨 1. 事务方法必须是 public
@Transactional // 正确
public void doSomething() {}
🚨 2. 方法 不能是同一个类中内部调用
// 错误示例:事务不起作用
public void outer() {this.inner(); // 没经过代理
}
✅ 正确做法:通过代理调用
- 注入自身(@Lazy方式)
- 拆到 Service 层让 Spring 来代理调用
🚨 3. 默认只对 运行时异常 回滚
@Transactional(rollbackFor = Exception.class)
🧪 八、事务管理器(Spring 内部机制)
Spring 通过 PlatformTransactionManager 接口进行统一管理。
常用实现类:
| 实现类 | 场景 | 
|---|---|
| DataSourceTransactionManager | 使用 JDBC、MyBatis | 
| JpaTransactionManager | 使用 JPA/Hibernate | 
| ChainedTransactionManager | 多数据源事务 | 
✅ 九、一句话总结
Spring 的事务机制让你只关心业务逻辑,不用手动管理事务,声明式的方式简洁高效,是真正的企业级利器。
@Transactional 实现原理
它的实现原理非常巧妙,实际上是通过 AOP(面向切面编程) 和 代理机制 实现的。这一机制让你无需写一行事务管理的代码,Spring 会自动为你处理。
我们一起来详细剖析一下底层原理!🧠
💡 一、核心原理概览
1. AOP + 代理:
@Transactional 注解是基于 AOP(面向切面编程)实现的,具体来说,Spring 会为标记了 @Transactional 注解的方法创建一个代理对象,这个代理对象会在方法执行前后进行事务的开启、提交和回滚等处理。
2. 动态代理:
Spring 通过动态代理技术(JDK 代理或 CGLIB 代理)生成一个代理对象,这个对象会拦截你的方法调用,在方法调用前后进行事务管理的相关操作。
3. 事务管理器:
Spring 会利用配置的 事务管理器(PlatformTransactionManager) 来处理事务的开启、提交、回滚等操作。
🛠️ 二、@Transactional 的工作流程
 
步骤 1:创建代理对象
- 代理方式:Spring 使用 JDK 动态代理 或 CGLIB 代理 来生成代理对象。默认情况下,如果目标类实现了接口,Spring 会使用 JDK 动态代理;如果没有实现接口,则使用 CGLIB 创建子类代理。
步骤 2:事务管理器的配置
- @Transactional依赖于 事务管理器 来执行事务操作,Spring 根据你配置的- PlatformTransactionManager来管理事务。常见的事务管理器有:- DataSourceTransactionManager:用于 JDBC。
- JpaTransactionManager:用于 JPA(如 Hibernate)。
- HibernateTransactionManager:用于 Hibernate。
 
步骤 3:方法执行前,开启事务
当你调用被 @Transactional 注解标记的方法时,Spring 代理会拦截这个方法的调用,首先会判断当前方法是否需要开启新事务(即,方法的传播行为是 REQUIRED)。如果需要开启新事务,则会通过 事务管理器 启动一个新的事务。
- 事务的传播行为(propagation):比如,REQUIRED表示如果当前没有事务,就新建一个事务,如果已有事务,就加入到当前事务中。
步骤 4:方法执行中,进行事务操作
方法执行过程中,Spring 会维持该事务的状态,直到方法执行完毕。
- 事务隔离级别(isolation):隔离级别决定了事务之间如何互相影响(比如,是否允许脏读、不可重复读等)。
步骤 5:方法执行后,提交或回滚事务
- 方法正常完成:如果方法执行没有异常,Spring 会在方法结束后提交事务。
- 方法出现异常:如果方法抛出异常,Spring 会根据 @Transactional配置的异常回滚规则,判断是否回滚该事务。
⚙️ 三、事务管理的关键组件
1. TransactionInterceptor:核心事务拦截器
- Spring 使用 TransactionInterceptor来处理所有带有@Transactional注解的方法,它是事务管理的关键拦截器。
- 该拦截器负责从容器中获取事务管理器、根据事务配置设置事务属性(如隔离级别、传播行为等),并且控制事务的开启、提交和回滚。
2. TransactionProxyFactoryBean:生成代理对象
- TransactionProxyFactoryBean是 Spring 用来创建事务代理的工厂,实际上它会根据注解或配置生成一个代理对象。
- 这个代理对象会拦截对事务方法的调用,并在调用方法前后进行事务的处理。
3. PlatformTransactionManager:事务管理器
- 事务管理器(如 DataSourceTransactionManager)负责实际的事务操作,包括开启事务、提交事务、回滚事务等。
🔍 四、事务处理的实际步骤
- 方法调用被代理对象拦截 - 被 @Transactional标记的方法会被 AOP 代理拦截,调用TransactionInterceptor。
 
- 被 
- 事务拦截器执行逻辑 - TransactionInterceptor会根据- @Transactional配置,从容器中获取 事务管理器(- PlatformTransactionManager)。
 
- 判断事务传播行为 - 如果当前没有事务(例如没有正在进行的事务),根据传播行为决定是否新建事务。默认是 REQUIRED,即如果没有事务,创建一个新的事务。
 
- 如果当前没有事务(例如没有正在进行的事务),根据传播行为决定是否新建事务。默认是 
- 开启事务 - 通过 PlatformTransactionManager开启一个事务(实际是对底层数据库操作的封装)。
 
- 通过 
- 执行目标方法 - 目标方法执行,在方法内的操作都在同一个事务中进行。
 
- 提交或回滚事务 - 如果方法执行没有抛出异常,Spring 提交事务;如果抛出指定异常(如运行时异常),Spring 会回滚事务。
 
💡 五、示例:@Transactional 事务的底层工作
 
假设你有一个服务类 AccountService,方法 transfer() 被 @Transactional 注解标记:
@Service
public class AccountService {@Transactionalpublic void transfer() {// 扣款操作// 加款操作// 如果中间出错,Spring 会自动回滚}
}
- Spring 会生成一个代理对象 AccountService,拦截transfer()方法调用。
- 代理对象通过 TransactionInterceptor来控制事务的开启、提交或回滚。
- 在调用 transfer()方法之前,Spring 会检查是否需要开启事务,创建事务并将其绑定到当前线程(一般是通过ThreadLocal来绑定)。
- transfer()执行完后,如果没有异常,Spring 会提交事务;如果出现异常,Spring 会根据配置回滚事务。
🧠 六、总结
@Transactional的底层原理依赖于 AOP + 动态代理,通过 事务拦截器(TransactionInterceptor)和 事务管理器(PlatformTransactionManager)来实现事务的控制。Spring 提供了灵活的事务传播行为和隔离级别,让事务控制变得更加简单和清晰。
Spring 事务在什么情况下会失效?
1. 方法是 private 或 protected
 
Spring 的事务管理是基于 AOP(面向切面编程) 实现的,而 AOP 需要代理对象来执行方法。如果方法是 private 或 protected,Spring 的 AOP 代理是无法拦截到该方法的,因此事务无法生效。
解决方法:
- 将事务方法设置为 public,这样 Spring 的代理对象才能正确地拦截并应用事务。
@Transactional
public void transfer() { // 必须是 public// 业务逻辑
}
2. 在同一类内调用事务方法
当一个类内部的事务方法相互调用时,事务是不会生效的。这是因为 事务代理 是基于代理对象的,而类内部方法调用是直接调用对象的方法,不会经过代理。
例子:
@Service
public class AccountService {@Transactionalpublic void transfer() {// 扣款操作this.otherMethod(); // 调用同一个类中的方法}@Transactionalpublic void otherMethod() {// 加款操作}
}
在这个例子中,transfer() 调用了 otherMethod(),但 @Transactional 注解没有生效,因为调用 this.otherMethod() 是直接调用类内部的方法,不会经过代理。
解决方法:
- 拆分方法,将事务性方法提取到另一个服务类中,确保 Spring 代理对象能够接管方法调用。
3. 没有配置事务管理器
Spring 中的事务是依赖于 PlatformTransactionManager 来进行管理的。如果没有配置或注入正确的事务管理器,事务就无法生效。
解决方法:
- 确保在 applicationContext.xml或Spring Boot配置类中配置了正确的事务管理器。
Spring Boot 示例:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {@Beanpublic PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {return new JpaTransactionManager(entityManagerFactory);}
}
4. 事务方法抛出了非 RuntimeException 异常
 
Spring 默认只对 运行时异常 (RuntimeException 和其子类) 回滚事务。如果你在方法中抛出了一个 检查型异常(checked exception)(如 IOException、SQLException 等),默认情况下,Spring 不会回滚事务。
解决方法:
- 使用 @Transactional的rollbackFor属性来指定哪些异常需要回滚事务。
@Transactional(rollbackFor = Exception.class)  // 指定回滚异常
public void transfer() throws IOException {// 业务逻辑
}
5. 事务被 @Transactional 注解的代理方法外部调用
 
Spring 的事务管理依赖于 代理对象,如果事务方法被其他 非 Spring 管理的对象调用,Spring 的事务也不会生效。
解决方法:
- 确保事务方法仅通过 Spring 管理的代理对象来调用。避免手动调用 new创建的对象或外部非 Spring 管理的对象。
6. 事务的传播行为不正确
事务的传播行为(propagation)控制了事务的嵌套和传播方式。如果设置不当,事务可能不会按预期工作。
例如,如果你使用了 REQUIRES_NEW 传播行为,每次方法都会启动一个新的事务,原事务会被挂起。如果这时原事务出现问题,它就无法回滚。
解决方法:
- 确保事务的传播行为与业务场景相匹配。常见的设置是 REQUIRED(默认),它会加入当前事务,如果没有事务,则创建一个新的事务。
@Transactional(propagation = Propagation.REQUIRED)
public void transfer() {// 事务逻辑
}
7. 非事务方法调用事务方法时,事务失效
如果一个非事务方法调用了带有 @Transactional 的方法,在一些情况下事务不会生效。特别是在 非 Spring 管理的对象 上,事务不会生效。
解决方法:
- 确保事务方法是由 Spring 管理的代理对象来调用,避免在非 Spring 管理的对象中调用事务方法。
8. 只读事务修改数据
@Transactional 中的 readOnly 属性告诉 Spring 该事务是只读的。如果在只读事务中执行了写操作(比如更新数据库),在某些数据库系统中事务可能不会按预期提交,甚至会抛出异常。
解决方法:
- 确保只读事务只用于查询操作,避免在只读事务中执行写操作。
@Transactional(readOnly = true)
public List<User> getAllUsers() {return userRepository.findAll();
}
🚦 九、总结
Spring 事务失效的常见原因:
- 事务方法是 private或protected。
- 方法在同一类中被调用(直接调用,未通过代理)。
- 没有配置事务管理器。
- 抛出了非 RuntimeException异常。
- 事务方法被非 Spring 管理的对象外部调用。
- 事务的传播行为设置不当。
- 非事务方法调用事务方法时,事务失效。
- 只读事务进行数据修改。
避免失效的建议:
- 确保事务方法是 public。
- 使用 Spring 管理的代理对象来调用事务方法。
- 正确配置事务管理器。
- 根据需求调整 rollbackFor和propagation配置。
- 使用 readOnly标记仅进行查询的事务。