当前位置: 首页 > news >正文

Spring事务失效场景?

题目详细答案

Spring事务失效的场景主要有以下几种。

非public方法使用@Transactional

场景描述:Spring事务管理是基于AOP实现的,而AOP对于JDK动态代理CGLib动态代理只会代理public方法。如果事务方法的访问修饰符为非public,SpringAOP无法正确地代理该方法,从而导致事务失效。

示例代码:事务方法的访问修饰符被设置为private、default或protected。

解决方案:将需要事务管理的方法设置为public。

在同类中的非事务方法调用事务方法

场景描述:Spring的事务管理是通过动态代理实现的,只有通过代理对象调用的方法才能享受到Spring的事务管理。如果在同一个类中,一个没有标记为@Transactional的方法内部调用了一个标记为@Transactional的方法,那么事务是不会起作用的。

解决方案:尽量将事务方法放在不同的类中,或者使用Spring的AopContext.currentProxy()来获取当前类的代理对象,然后通过代理对象调用事务方法。

事务传播级别属性设置不当

场景描述:在Spring的事务管理中,如果在一个支持当前事务的方法(比如,已经被标记为@Transactional的方法)中调用了一个需要新事务的方法,如果后者方法抛出了异常,但异常并未被Spring识别为需要回滚事务的异常,那么后者的事务将不会回滚。

异常类型不匹配

场景描述:默认情况下,Spring只有在方法抛出运行时异常或者错误时才会回滚事务。对于检查性异常,即使你在方法中抛出了,Spring也不会回滚事务,除非你在@Transactional注解中显式地指定需要回滚哪些检查性异常。

解决方案:了解Spring事务管理对异常的处理,必要时在@Transactional注解中指定需要回滚的异常类型。

事务拦截器配置错误

场景描述:如果没有正确地配置事务拦截器,例如没有指定切入点或指定了错误的切入点,就会导致Spring事务失效。

@EnableTransactionManagement
@Configuration
public class TxConfig {@Beanpublic Advisor transactionAdvisor() {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* com..service.*.*(..))"); // 包路径错误return new DefaultPointcutAdvisor(pointcut, transactionInterceptor());}
}

事务超时配置错误

场景描述:如果事务超时时间设置得太短,就有可能在事务执行过程中出现超时,从而导致Spring事务失效。

Spring事务超时是通过JDBC的java.sql.Connection#setNetworkTimeout()实现的,底层流程:

if (System.currentTimeMillis() - startTime > timeoutMillis) {throw new TransactionTimedOutException("Transaction timed out");
}

Spring事务失效场景深度解析

一、非public方法失效的本质原因

底层机制

  1. 代理生成限制
// Spring源码中的判断逻辑
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null; // 直接返回不创建事务属性
}
    • JDK动态代理只能代理接口中的public方法
    • CGLIB虽然能代理类方法,但Spring主动限制了非public方法的事务支持
  1. 设计考量
    • 非public方法通常被视为内部实现细节
    • 保持事务边界清晰(public方法才是服务契约)

典型错误示例

@Service
public class PaymentService {@Transactionalprotected void processPayment() {  // protected方法// 事务不会生效!}
}

二、同类调用失效的代理机制

核心问题图解

[客户端] --> [Spring代理对象] --> [真实对象]调用createOrder()       this.validate()绕过代理

字节码层面分析

  1. 代理对象生成后,方法调用流程:
    • 外部调用:proxy.createOrder()
    • 内部调用:realObject.validate()(直接调用,不走代理)

解决方案对比

方案

优点

缺点

拆分类

符合单一职责原则

增加类数量

自注入

改动最小

存在循环依赖风险

AopContext.currentProxy()

灵活

需配置exposeProxy=true

三、传播行为配置陷阱

REQUIRES_NEW的隔离性

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditLog() {// 即使外层事务回滚,此操作仍会提交// 但会创建新连接,可能耗尽连接池
}

NESTED的数据库支持

  • 支持:MySQL、PostgreSQL
  • 部分支持:Oracle(行为差异)
  • 不支持:MyISAM引擎

四、异常处理不当的判定逻辑

Spring回滚决策树

  1. 检查rollbackFor/noRollbackFor明确指定的异常
  2. 默认规则:
    • RuntimeException及其子类 → 回滚
    • Error及其子类 → 回滚
    • 检查型异常(Exception) → 提交

易错场景示例

@Transactional
public void importData() throws IOException {try {parseFile(); // 可能抛出IOException} catch (IOException e) {throw new BusinessException(e); // 必须转换为RuntimeException}
}

五、数据库引擎关键影响

事务支持矩阵

引擎特性

InnoDB

MyISAM

Memory

事务支持

✔️

✖️

✖️

行级锁

✔️

✖️

✖️

外键支持

✔️

✖️

✖️

检查方法

SHOW TABLE STATUS WHERE Name='table_name';

六、静态/最终方法限制

JVM方法调用机制

  • final方法:静态绑定,编译期确定调用目标
  • static方法:类级别调用,与对象实例无关
  • 二者都无法被动态代理拦截

事务调试四步法

  1. 检查代理类型
System.out.println(service.getClass().getName());
// 应输出包含$$EnhancerBySpringCGLIB$$或$Proxy的类名
  1. 验证事务状态
TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.getCurrentTransactionName();
  1. 开启事务日志
logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.jdbc=DEBUG
  1. 数据库监控
SHOW ENGINE INNODB STATUS;  -- MySQL
SELECT * FROM V$TRANSACTION; -- Oracle

最佳实践清单

  1. 编码规范
    • 所有事务方法必须为public
    • 避免在事务方法中捕获所有异常
    • 显式指定rollbackFor
  1. 配置检查
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class TxConfig {@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}
}
  1. 测试验证
@Test
@Transactional
public void testWithRollback() {// 测试后自动回滚assertThrows(Exception.class, () -> service.method());
}

理解这些原理后,可以系统性地避免事务失效问题,而不仅是记住表面现象。实际开发中建议结合日志监控和单元测试,确保事务行为符合预期。

http://www.lryc.cn/news/612639.html

相关文章:

  • 【基础知识】springboot+vue 基础框架搭建(更新中)
  • 下载 | Windows Server 2016最新原版ISO映像!(集成7月更新、标准版、数据中心版、14393.8246)
  • MacOS Docker 安装指南
  • 进程、网络通信方法
  • 在Linux下访问MS SQL Server数据库
  • 机器学习工程化 3.0:从“实验科学”到“持续交付”的 7 个关卡
  • 【2025最新】在 macOS 上构建 Flutter iOS 应用
  • 函数、方法和计算属性
  • 「iOS」————持久化
  • HashMap寻址算法
  • 如何显示一个 Elasticsearch 索引的字段
  • Opencv-管理图片
  • 冷库温湿度物联网监控系统解决方案:冷链智能化
  • [无需 Mac] 使用 GitHub Actions 构建 iOS 应用
  • 嵌入式硬件学习(十一)—— platform驱动框架
  • 嵌入式硬件中MOSFET基本原理与实现
  • 区块链技术原理(2) -数据结构
  • 嵌入式硬件中MOSFET基本控制详解
  • 「iOS」————自动释放池底层原理
  • 基于Python+Vue+Mysql实现(物联网)智能大棚
  • C 语言主控开发与显控开发能力体系及技术栈详解,STM32、QT、嵌入式、边缘系统显示
  • Spring、Spring MVC、MyBatis 和 Spring Boot的关系
  • STM32U5 周期性异常复位问题分析
  • 物联网架构全解析:华为“1+2+1”与格行随身WiFi,技术如何定义未来生活?
  • JVM学习日记(十七)Day17——性能监控与调优(四)
  • .NET9 AOT完全自举了吗?
  • .NET 10 新增功能系列文章5——C# 14 中的新增功能
  • Unity URP渲染管线动态修改材质球状态
  • 38.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--扩展功能--增加日志记录器
  • 十八、k8s细粒度流量管理:服务网格