首页 > Java > java教程 > 正文

Spring中的事务管理机制是如何工作的?

夢幻星辰
发布: 2025-09-03 21:00:01
原创
977人浏览过
Spring通过AOP与PlatformTransactionManager抽象实现事务管理,以@Transactional注解为核心,支持声明式与编程式事务,利用代理机制在方法前后织入事务逻辑,确保ACID特性。

spring中的事务管理机制是如何工作的?

Spring的事务管理机制,说到底,就是提供了一套高度抽象且灵活的方式,来处理数据库操作或其他资源操作的原子性、一致性、隔离性和持久性(ACID)特性。它将底层各种事务API(比如JDBC、JPA、JTA)的复杂性屏蔽掉,让开发者可以用统一的、通常是声明式的方式,来定义业务方法的事务行为,极大地简化了开发。核心在于,Spring通过AOP(面向切面编程)在方法执行前后织入事务逻辑,根据配置自动开启、提交或回滚事务。

解决方案

Spring的事务管理主要围绕几个核心概念和实现方式展开。

首先是事务抽象层。Spring定义了

PlatformTransactionManager
登录后复制
接口作为事务管理的核心抽象,它负责创建、提交和回滚事务。不同的底层事务技术(如JDBC、JPA、JTA)都有其对应的
PlatformTransactionManager
登录后复制
实现,例如
DataSourceTransactionManager
登录后复制
用于JDBC,
JpaTransactionManager
登录后复制
用于JPA,
JtaTransactionManager
登录后复制
用于JTA。此外,
TransactionDefinition
登录后复制
定义了事务的各种属性,如传播行为、隔离级别、超时时间以及是否只读。
TransactionStatus
登录后复制
则代表了一个正在进行的事务的状态。

接着是声明式事务管理,这是Spring最常用也是推荐的方式。它通过AOP技术,在不修改业务代码的情况下,为方法添加事务能力。 最常见的是使用

@Transactional
登录后复制
注解。你可以在类级别或方法级别应用这个注解。当Spring容器启动时,会扫描这些注解,并为带有
@Transactional
登录后复制
注解的Bean创建代理对象。当通过这个代理对象调用被注解的方法时,代理会在方法执行前启动一个事务,方法执行成功后提交事务,如果发生异常则回滚事务。 除了注解,也可以通过XML配置来声明事务,通常使用
<tx:advice>
登录后复制
结合
<aop:config>
登录后复制
,这种方式在早期项目或需要更细粒度控制时会用到,但现在注解方式更为流行和简洁。

最后是编程式事务管理,虽然不如声明式常用,但在某些特殊场景下(比如事务逻辑非常复杂,或者需要在事务内部进行更细致的控制)仍有其价值。 一种方式是直接使用

PlatformTransactionManager
登录后复制
接口。你可以手动获取一个
PlatformTransactionManager
登录后复制
实例,然后调用它的
getTransaction()
登录后复制
commit()
登录后复制
rollback()
登录后复制
方法来控制事务。 另一种更优雅的编程式方式是使用
TransactionTemplate
登录后复制
。它封装了
PlatformTransactionManager
登录后复制
的复杂性,提供了一个回调接口,你只需要在回调中编写业务逻辑,
TransactionTemplate
登录后复制
会负责事务的开启、提交和回滚。

Spring事务管理中,
@Transactional
登录后复制
注解究竟是怎么生效的?

这其实是Spring AOP的一个经典应用。当你把

@Transactional
登录后复制
注解加到某个Service类或其方法上时,Spring容器并不会直接修改你的源代码。相反,它会在运行时为这个Bean生成一个代理对象。这个代理对象才是真正被其他组件(比如Controller)引用的对象。

当外部组件调用被

@Transactional
登录后复制
注解的方法时,实际上是调用了代理对象的方法。代理对象内部有一个事务拦截器(TransactionInterceptor)。这个拦截器在方法执行前,会根据
@Transactional
登录后复制
注解的属性(比如传播行为、隔离级别、是否只读等),向
PlatformTransactionManager
登录后复制
请求开启一个事务。如果事务成功开启,它会记录下事务状态,然后才真正调用目标Service对象的业务方法。

业务方法执行完毕后,拦截器会检查方法执行结果。如果方法正常返回,拦截器会通知

PlatformTransactionManager
登录后复制
提交事务。如果方法执行过程中抛出了异常(特别是运行时异常,或者配置了
rollbackFor
登录后复制
的特定异常),拦截器会捕获这个异常,并通知
PlatformTransactionManager
登录后复制
回滚事务。最后,它会重新抛出异常,让上层调用者感知到。

这个代理的生成方式,通常有两种:

  1. JDK动态代理:如果你的Bean实现了接口,Spring默认会使用JDK动态代理。它会为接口生成一个实现类,这个实现类就是代理对象。
  2. CGLIB代理:如果你的Bean没有实现接口,或者你明确配置了使用CGLIB,Spring会使用CGLIB库来生成目标类的子类作为代理对象。

理解这一点很重要,因为它解释了为什么在同一个Service内部,一个没有

@Transactional
登录后复制
注解的方法去调用另一个带有
@Transactional
登录后复制
注解的方法时,后者注解的事务行为可能不会生效。这是因为内部调用是直接通过
this
登录后复制
指针进行的,绕过了代理对象,事务拦截器也就无从介入了。

理解Spring事务的传播行为:不同模式下有什么区别和应用场景?

事务的传播行为(Propagation Behavior)定义了当一个事务方法被另一个事务方法调用时,事务如何进行管理。这是Spring事务管理中一个非常关键且容易混淆的概念。理解它们能帮助我们避免一些微妙的事务问题。

  • REQUIRED
    登录后复制
    (默认值):这是最常用也最直观的模式。如果当前没有事务,就创建一个新事务;如果当前已经有事务在运行,就加入到这个现有事务中。这意味着,所有被
    REQUIRED
    登录后复制
    修饰的方法,无论被哪个事务方法调用,最终都会在一个事务中运行。这是确保一组操作原子性的最简单方式。

    • 应用场景:绝大多数业务操作,例如一个订单的创建流程,其中涉及多个数据库写入操作,都应该在一个事务中。
  • REQUIRES_NEW
    登录后复制
    :无论当前是否存在事务,都创建一个全新的事务。如果当前已经有事务在运行,那么这个现有事务会被挂起,新的事务独立运行,执行完毕后,原先的事务会恢复。这意味着
    REQUIRES_NEW
    登录后复制
    方法总是在自己的事务中执行,与其他事务完全隔离。

    • 应用场景:日志记录。你可能希望无论主业务操作成功与否,日志记录都能独立地提交。或者,一个操作的某个子步骤,即使失败也不应影响主事务的回滚,反之亦然。例如,在一个复杂审批流程中,某个子流程的审批状态更新,即使失败也不应该导致整个主流程回滚。
  • SUPPORTS
    登录后复制
    :如果当前有事务,就加入到这个事务中;如果没有事务,就以非事务方式执行。

    如此AI写作
    如此AI写作

    AI驱动的内容营销平台,提供一站式的AI智能写作、管理和分发数字化工具。

    如此AI写作112
    查看详情 如此AI写作
    • 应用场景:读取操作。很多时候,查询数据并不需要强制在一个事务中执行,但如果当前已经有事务,顺便加入也无妨。
  • NOT_SUPPORTED
    登录后复制
    :以非事务方式执行操作。如果当前有事务,就将这个事务挂起。

    • 应用场景:与
      REQUIRES_NEW
      登录后复制
      类似,但更强调“不参与事务”。例如,发送邮件或短信通知,这些操作通常不希望受到数据库事务回滚的影响。
  • MANDATORY
    登录后复制
    :必须在一个已经存在的事务中运行。如果当前没有事务,则抛出异常。

    • 应用场景:某个核心业务方法,它明确依赖于一个外部已经开启的事务。例如,一个更新库存的方法,它必须在创建订单的事务中才能执行。
  • NEVER
    登录后复制
    :以非事务方式执行操作。如果当前有事务,则抛出异常。

    • 应用场景:与
      NOT_SUPPORTED
      登录后复制
      类似,但更严格。明确禁止在事务中执行某些操作,例如某些外部系统调用。
  • NESTED
    登录后复制
    :如果当前有事务,则在嵌套事务内执行。嵌套事务是当前事务的一个子事务,它有自己的保存点(savepoint)。如果子事务失败,可以回滚到这个保存点,而不会影响外部事务的提交。如果外部事务回滚,嵌套事务也会跟着回滚。如果没有事务,则行为与
    REQUIRED
    登录后复制
    类似。

    • 应用场景:一个复杂业务流程中的某个可选步骤。这个步骤可以独立回滚而不影响主流程,但如果主流程整体失败,这个步骤也必须跟着回滚。例如,一个电商订单的积分抵扣,如果抵扣失败,可以回滚抵扣操作,但订单主流程可以继续;但如果订单最终取消,积分抵扣也必须撤销。需要注意的是,
      NESTED
      登录后复制
      通常只在支持JDBC Savepoint的事务管理器(如
      DataSourceTransactionManager
      登录后复制
      )下才有效。

Spring事务管理中,哪些常见陷阱和性能考量需要注意?

在使用Spring事务管理时,虽然它极大地方便了开发,但如果不注意一些细节,也容易踩坑或引入性能问题。

一个非常经典的陷阱是同一对象内部方法调用(Self-invocation)。如果你在Service层的一个

@Transactional
登录后复制
方法A中,直接通过
this
登录后复制
调用了同一个Service类中的另一个
@Transactional
登录后复制
方法B,那么方法B的事务注解可能不会生效。原因在于,
this
登录后复制
调用绕过了Spring为该Service生成的代理对象。事务逻辑是由代理对象拦截并织入的,直接调用
this
登录后复制
会直接执行原始对象的方法,事务拦截器就无法介入了。解决办法通常是将方法B移动到另一个Service中,或者通过AopContext获取当前代理对象来调用。

另一个常见的点是异常类型与事务回滚。默认情况下,Spring事务只对运行时异常(

RuntimeException
登录后复制
及其子类)和
Error
登录后复制
进行回滚,而对受检异常(Checked Exception)不回滚。这意味着如果你在事务方法中抛出了一个
IOException
登录后复制
或自定义的受检异常,事务可能不会回滚。如果你希望对特定受检异常也进行回滚,需要明确在
@Transactional
登录后复制
注解中指定
rollbackFor
登录后复制
属性,例如
@Transactional(rollbackFor = MyCheckedException.class)
登录后复制
。反之,如果你不希望对某个运行时异常回滚,可以使用
noRollbackFor
登录后复制

事务管理器配置不当也会导致问题。例如,在JPA项目中使用

DataSourceTransactionManager
登录后复制
而不是
JpaTransactionManager
登录后复制
,会导致事务无法正确地与JPA的
EntityManager
登录后复制
同步,从而出现数据不一致。确保你为你的持久层技术配置了正确的
PlatformTransactionManager
登录后复制
实现。

性能考量方面,隔离级别是一个重要因素。更高的隔离级别(如

SERIALIZABLE
登录后复制
)可以提供更强的数据一致性,但代价是会引入更多的锁,降低并发性能。在大多数Web应用中,
READ_COMMITTED
登录后复制
REPEATABLE_READ
登录后复制
通常是比较平衡的选择。理解你的业务需求,选择合适的隔离级别至关重要。不必要的强隔离级别可能会导致数据库死锁或性能瓶颈。

长事务也应尽量避免。一个事务持续时间过长,会长时间占用数据库连接,持有锁,这不仅会消耗数据库资源,还可能阻塞其他事务,导致整个系统的吞吐量下降。设计业务逻辑时,尽量将事务的范围控制在最小必要的粒度,快速开启、快速提交或回滚。对于需要长时间处理的业务,可以考虑拆分成多个小事务,或者引入消息队列等异步机制。

最后,只读事务是一个简单的优化手段。对于那些只涉及数据查询而不涉及修改的业务方法,可以将其标记为

@Transactional(readOnly = true)
登录后复制
。这会向底层数据库驱动或ORM框架(如Hibernate)提供一个提示,允许它们进行一些性能优化,例如不设置事务锁,或者使用更轻量级的数据库连接。虽然效果不总是立竿见影,但在高并发的读操作场景下,积累起来的优化还是有价值的。

以上就是Spring中的事务管理机制是如何工作的?的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号