深入理解spring如何管理事务
作者:小贝壳O.o
Spring 事务管理概述
最先需要明确一点的是,事务只是数据库中的一个概念,我们的java程序和数据库进行连接,java程序只是作为数据库的客户端,本质是通过网络连接对象connection来间接的操作数据库对sql进行事务开启,提交或者回滚。在 Spring 中,事务管理通常分为两种方式:编程式事务和声明式事务。其中,声明式事务(基于注解 @Transactional
)是最常用、最直观的做法。本质上,Spring 通过 AOP(面向切面编程) 或 代理机制 来拦截方法调用,在方法执行前后自动开启、提交或回滚事务。
1. Spring 事务管理的整体架构
1.1 PlatformTransactionManager接口
- Spring 为各种数据访问框架提供了不同实现的 PlatformTransactionManager,例如:
DataSourceTransactionManager
(JDBC)JpaTransactionManager
(JPA/Hibernate)HibernateTransactionManager
(Hibernate)JpaTransactionManager
(JPA)以及其他第三方框架的事务管理器。
- 这些事务管理器对底层资源(数据库连接、Session 等)进行事务控制,Spring 通过它们来统一管理事务的开启、提交与回滚。
1.2 事务配置(Transaction Configuration)
- 通过注解
@EnableTransactionManagement
或者在 XML 中声明<tx:annotation-driven>
,开启 Spring 对声明式事务的支持。 - 配置好对应的
PlatformTransactionManager
Bean,让 Spring 知道使用哪一种事务管理器。
1.3 声明式事务(@Transactional)
- Spring 在运行时扫描被
@Transactional
注解标记的类或方法,自动为其生成 AOP 代理。 - 当外部调用该方法时,会先进入 事务切面,根据注解信息决定是否开启事务,执行完毕后再根据方法执行结果决定提交还是回滚。
1.4 事务增强(Advice)
- 事务的开启、提交、回滚等逻辑,就封装在 Spring 提供的事务增强(
TransactionInterceptor
)中,通过 AOP 动态拦截目标方法调用。
2. Spring 事务管理的关键流程
以下主要针对 声明式事务(基于注解)进行说明:
2.1 Bean 初始化阶段
- Spring 在启动时,会扫描所有的 Bean,看它们是否使用了
@Transactional
注解。 - 如果某个类或方法使用了
@Transactional
,则由 Spring AOP 或 AspectJ 生成一个代理对象来代替原始对象。 - 代理对象内部持有对真实对象的引用,同时嵌入事务管理逻辑。
2.2 方法调用拦截(AOP 代理)
- 当外部代码调用该 Bean 的某个带有
@Transactional
的方法时,实际上先调用到 代理对象。 - 代理对象 会进行一系列判断,包括:
- 读取
@Transactional
的属性(如propagation
、isolation
、timeout
、readOnly
等); - 判断当前线程是否已经存在事务(如果有,则根据
propagation
属性决定如何处理); - 如果没有事务或者需要新建事务,则调用
PlatformTransactionManager
的getTransaction(...)
开启一个事务。
- 读取
2.3 执行目标方法
在事务已开启(或已决定加入现有事务)的前提下,代理对象通过反射调用目标 Bean 的真实方法。目标方法中执行数据库操作(或其他资源操作),如果抛出异常,Spring 会捕获并根据 rollback rules(例如:RuntimeException
、Error
默认回滚等)决定是否回滚。
2.4 提交或回滚事务
- 如果目标方法正常结束,Spring 在退出代理方法前,会调用
PlatformTransactionManager
的commit(...)
方法,提交事务; - 如果出现了异常且符合回滚条件,Spring 调用
PlatformTransactionManager
的rollback(...)
方法,回滚事务; - 此后,方法调用才真正返回给调用者。
2.5 释放资源
- 事务提交或回滚之后,底层数据库连接、Session 等资源会被释放或归还到连接池。
- Spring 也会在内部进行相应的清理操作,确保线程上下文不再持有错误的事务信息。
3. 事务属性与配置
在使用 @Transactional
注解时,可以指定多个属性来控制事务行为,比如:
@Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 30, readOnly = false, rollbackFor = {Exception.class}, noRollbackFor = {CustomException.class} ) public void doSomething() { // ... }
propagation - 事务传播级别
常见值:REQUIRED
、REQUIRES_NEW
、SUPPORTS
、MANDATORY
、NESTED
等,决定当前方法是否需要在已有事务中执行,或新开一个事务等。
isolation - 事务隔离级别
如 READ_UNCOMMITTED
, READ_COMMITTED
, REPEATABLE_READ
, SERIALIZABLE
,决定事务间读写数据的可见性,防止脏读、不可重复读、幻读等。
timeout - 超时时间
指定事务执行的最大时间(秒),超过时间未完成则抛出超时异常并回滚。
readOnly - 只读标志
指示该事务主要用于查询操作,可能在某些情况下允许数据库做额外优化;某些数据库会忽略此配置。
rollbackFor / noRollbackFor - 回滚策略
指定哪些异常抛出时应回滚或不回滚;默认对 运行时异常 和 错误 回滚,对编译异常 不回滚。
4. 实际开发中事务的常见问题
同类方法相互调用失效
如果在方法 A 内部调用方法 B,而 B 也使用了 @Transactional
,但这两者都属于同一个类,则 B 的事务不会被代理拦截,导致事务注解失效。可以通过将 B 提取到另一个 Bean 或使用 AspectJ 等方式解决。
异常类型导致事务不回滚
Spring 默认对 RuntimeException
或 Error
回滚,而对受检异常(Checked Exception)不回滚,需要在注解中显式指定 rollbackFor
。不小心抛出了“错误类型”的异常,就可能导致事务回滚意外。
读写操作混用导致性能或数据一致性问题@Transactional(readOnly = true)
仅作提示,不会强制阻止写操作。如果在此事务中执行了写操作或忘记设置 readOnly = false
,就可能引发数据不一致或性能问题。
事务传播属性配置不当
如果不小心将一个需要新事务的操作配置为 Propagation.REQUIRED
而不是 REQUIRES_NEW
,或者反之,则会导致事务边界不符预期,进而出现脏数据、锁竞争等问题。
大事务导致锁竞争
在实际项目里,如果单次事务持续时间过长,可能会长时间占用数据库锁,导致锁竞争或死锁;需注意将大事务拆分或优化为多次小事务。
多线程/异步场景下无法共享事务
Spring 的默认事务机制基于 ThreadLocal
绑定连接,异步任务或多线程无法复用同一事务,如果你在一个方法里开启多线程进行并发数据库操作,子线程无法直接继承主线程的事务上下文,会拿不到同一个 Connection。
5.spring声明式事务执行过程示例图
6.总结
核心原理
Spring 通过 AOP 代理 拦截带有 @Transactional
的方法,在方法执行前后,根据注解属性和底层 PlatformTransactionManager
自动开启、提交或回滚事务。
关键组件
@Transactional
注解- 事务切面(
TransactionInterceptor
) PlatformTransactionManager
TransactionSynchronizationManager
(这是 Spring 用于管理“线程上下文”的关键工具类,记录当前线程关联的事务资源,如Connect。
同一事务内,所有数据库操作都共享此 Connection)
事务配置
可自定义传播级别、隔离级别、超时、回滚策略等,以满足不同的业务需要。
到此这篇关于spring如何管理事务的文章就介绍到这了,更多相关spring管理事务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!