ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

一文讲透 Spring 事务:传播行为、隔离级别与底层原理

一文讲透 Spring 事务:传播行为、隔离级别与底层原理

Spring 事务的核心作用,是保证一组数据库操作要么全部成功,要么全部失败。它在数据库事务之上提供了统一抽象,使开发者无须直接操作 JDBC 的提交和回滚。

一、Spring 事务的类型与管理方式

按资源范围划分,事务可分为两类:

  • 本地事务:只操作单个数据库或资源,常由DataSourceTransactionManagerJpaTransactionManager管理。

  • 全局事务:协调多个数据库或消息等资源,传统方案可使用 JTA。分布式系统中也可能采用 Seata 等方案。

按使用方式划分,Spring 支持两种事务管理方式:

  1. 编程式事务:通过TransactionTemplatePlatformTransactionManager手动控制,灵活但业务代码与事务代码耦合。

  2. 声明式事务:使用@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默认遇到未被捕获并传播到事务代理之外的RuntimeExceptionError会回滚;遇到受检异常默认不会回滚。

若希望所有业务异常都回滚,可以配置:

@Transactional(rollbackFor = Exception.class)

如果方法内部使用try-catch吞掉异常,代理感知不到异常,通常会正常提交。此时可以重新抛出异常,或调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()标记回滚。

六、JDK 动态代理与 CGLIB

在 Spring Framework 的常规代理规则中,目标类实现接口时可使用 JDK 动态代理;没有实现接口时使用 CGLIB,通过生成目标类的子类完成代理。也可以设置proxyTargetClass = true强制使用 CGLIB。

所以这不是运行时失败后的“降级切换”,而是创建代理时根据接口和配置进行选择。CGLIB 无法代理final类,也无法增强finalprivate等不能被子类重写的方法。Spring Boot 的默认代理配置可能与纯 Spring Framework 不同,面试时应说明具体环境。

七、Spring 5 之后多个 AOP 通知的顺序

对一次正常调用,可以记成:

@Around 前置 → @Before → 目标方法 → @AfterReturning → @After → @Around 后置

目标方法抛出异常时,@AfterThrowing替代@AfterReturning。从 Spring 5.2.7 开始,同一切面中会可靠地保证@AfterReturning@AfterThrowing先于@After执行。

多个切面之间应通过@OrderOrdered明确顺序:数值越小,进入时越先执行,退出时越后执行,整体类似过滤器嵌套。不要依赖同一切面中多个同类型通知的方法声明顺序,最好拆分切面或显式指定顺序。

八、总结

Spring 声明式事务以 AOP 代理为入口,通过事务管理器控制数据库事务,并在线程同步模型下借助ThreadLocal绑定事务资源。@Transactional默认传播行为是REQUIRED、隔离级别是DEFAULT,未捕获的运行时异常会触发回滚。掌握传播行为、回滚规则、代理边界和 AOP 顺序,才能避免“注解写了但事务没有生效”的问题。

参考资料:Spring 事务注解、Spring 事务抽象、Spring AOP 代理机制。

返回列表