Spring 事务的核心作用,是保证一组数据库操作要么全部成功,要么全部失败。它在数据库事务之上提供了统一抽象,使开发者无须直接操作 JDBC 的提交和回滚。
一、Spring 事务的类型与管理方式
按资源范围划分,事务可分为两类:
本地事务:只操作单个数据库或资源,常由
DataSourceTransactionManager、JpaTransactionManager管理。全局事务:协调多个数据库或消息等资源,传统方案可使用 JTA。分布式系统中也可能采用 Seata 等方案。
按使用方式划分,Spring 支持两种事务管理方式:
编程式事务:通过
TransactionTemplate或PlatformTransactionManager手动控制,灵活但业务代码与事务代码耦合。声明式事务:使用
@Transactional或 XML 配置,由 Spring AOP 自动管理。实际开发中更常用。
二、七种事务传播行为
传播行为解决的问题是:一个事务方法调用另一个事务方法时,新方法应该加入原事务,还是创建新事务?
| 传播行为 | 含义 |
|---|---|
REQUIRED | 有事务就加入,没有就新建,默认值 |
REQUIRES_NEW | 挂起当前事务,创建独立新事务 |
SUPPORTS | 有事务就加入,没有就以非事务方式运行 |
NOT_SUPPORTED | 挂起当前事务,以非事务方式运行 |
MANDATORY | 必须存在事务,否则抛出异常 |
NEVER | 必须不存在事务,否则抛出异常 |
NESTED | 在当前事务中创建保存点,失败可回滚到保存点 |
高频区别是:REQUIRES_NEW创建独立事务,内外事务分别提交;NESTED通常依赖外部物理事务和保存点,外部事务整体回滚时,嵌套部分也无法保留。
三、@Transactional的底层原理
@Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class ) public void createOrder() { orderMapper.insertOrder(); stockMapper.deductStock(); }@Transactional本身只是事务元数据,默认通过 Spring AOP 代理生效。外部调用代理对象时,事务拦截器先读取注解配置,再通过事务管理器获取数据库连接、关闭自动提交并开启事务。方法正常结束则提交,满足回滚规则的异常抛出则回滚。
同步事务中,Spring 通过ThreadLocal将连接等事务资源绑定到当前线程,使同一线程内的多个数据库操作共享同一个事务。最终真正提供原子性、隔离性和持久性的,仍是数据库事务机制。因此,事务上下文默认不会自动传播到新建线程;响应式事务则使用 Reactor Context,而非普通ThreadLocal。
默认代理模式还存在一个常见问题:同一个类中使用this调用另一个@Transactional方法会绕过代理,导致事务注解失效。
四、事务隔离级别
Spring 提供五种隔离级别:
| 隔离级别 | 作用 |
DEFAULT | 使用底层数据库的默认隔离级别 |
READ_UNCOMMITTED | 可能出现脏读、不可重复读和幻读 |
READ_COMMITTED | 避免脏读 |
REPEATABLE_READ | 避免脏读和不可重复读 |
SERIALIZABLE | 串行执行,并发能力最低 |
隔离级别越高,并发问题越少,但锁竞争和性能开销通常越大。MySQL InnoDB 默认是REPEATABLE_READ,但 Spring 的DEFAULT并不固定等于该级别,而是跟随当前数据库。
五、异常出现后是否回滚
@Transactional默认遇到未被捕获并传播到事务代理之外的RuntimeException或Error会回滚;遇到受检异常默认不会回滚。
若希望所有业务异常都回滚,可以配置:
@Transactional(rollbackFor = Exception.class)如果方法内部使用try-catch吞掉异常,代理感知不到异常,通常会正常提交。此时可以重新抛出异常,或调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()标记回滚。
六、JDK 动态代理与 CGLIB
在 Spring Framework 的常规代理规则中,目标类实现接口时可使用 JDK 动态代理;没有实现接口时使用 CGLIB,通过生成目标类的子类完成代理。也可以设置proxyTargetClass = true强制使用 CGLIB。
所以这不是运行时失败后的“降级切换”,而是创建代理时根据接口和配置进行选择。CGLIB 无法代理final类,也无法增强final、private等不能被子类重写的方法。Spring Boot 的默认代理配置可能与纯 Spring Framework 不同,面试时应说明具体环境。
七、Spring 5 之后多个 AOP 通知的顺序
对一次正常调用,可以记成:
@Around 前置 → @Before → 目标方法 → @AfterReturning → @After → @Around 后置目标方法抛出异常时,@AfterThrowing替代@AfterReturning。从 Spring 5.2.7 开始,同一切面中会可靠地保证@AfterReturning或@AfterThrowing先于@After执行。
多个切面之间应通过@Order或Ordered明确顺序:数值越小,进入时越先执行,退出时越后执行,整体类似过滤器嵌套。不要依赖同一切面中多个同类型通知的方法声明顺序,最好拆分切面或显式指定顺序。
八、总结
Spring 声明式事务以 AOP 代理为入口,通过事务管理器控制数据库事务,并在线程同步模型下借助ThreadLocal绑定事务资源。@Transactional默认传播行为是REQUIRED、隔离级别是DEFAULT,未捕获的运行时异常会触发回滚。掌握传播行为、回滚规则、代理边界和 AOP 顺序,才能避免“注解写了但事务没有生效”的问题。
参考资料:Spring 事务注解、Spring 事务抽象、Spring AOP 代理机制。