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

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.xdnf.cn/news/17201.html

相关文章:

  • TCP粘包问题详解与解决方案
  • 使用SETNX实现分布式锁
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘spacy’问题
  • 【C#补全计划:类和对象(九)】接口
  • 嵌入式开发硬件——单片机
  • QtC++ 中使用 qtwebsocket 开源库实现基于websocket的本地服务开发详解
  • Java中接口与抽象类
  • 【MATLAB】(十)符号运算
  • idea开发工具中git如何忽略编译文件build、gradle的文件?
  • 为什么 `source ~/.bashrc` 在 systemd 或 crontab 中不生效
  • 安卓开发:网络状态监听封装的奥秘
  • vLLM:彻底改变大型语言模型推理延迟和吞吐量
  • 【Apache Olingo】全面深入分析报告-OData
  • count(0),count(*),count(1),count(列)有什么区别?
  • Caffeine 三种过期策略详解
  • java - 深拷贝 浅拷贝
  • 大模型2位量化原理解析
  • 【线性代数】5特征值和特征向量
  • “认知裂缝边缘”地带
  • 共识算法介绍
  • DrissionPage自动化:高效Web操作新选择
  • uniapp-vue2导航栏全局自动下拉变色
  • 360纳米AI、实在Agent、CrewAI与AutoGen……浅析多智能体协作系统
  • 下载 | Windows Server 2016最新原版ISO映像!(集成7月更新、标准版、数据中心版、14393.8246)
  • 智能制造的中枢神经工控机在自动化产线中的关键角色
  • 恒虚警检测(CFAR)仿真:杂波边缘与多目标场景分析
  • 代码随想录算法训练营 Day20
  • Oracle 19C In-Memory 列存储技术测试
  • Numpy科学计算与数据分析:Numpy数组创建与应用入门
  • TypeScript 中高频出现的类型结构与用法