Spring声明式事务原理及事务失效场景?

Spring声明式事务原理

Spring声明式事务是基于AOP(面向切面编程)实现的。其核心流程如下:

1.事务管理器配置:

在Spring中,首先需要配置事务管理器,例如DataSourceTransactionManager(针对基于JDBC数据源的事务管理) 。它负责管理事务的开始、提交、回滚等操作,是事务管理的核心组件。

2.AOP切面织入:

Spring通过AOP机制,在方法调用前后织入事务相关逻辑。当一个标注了事务注解(如@Transactional)的方法被调用时,Spring会创建一个代理对象(基于JDK动态代理或CGLIB代理,取决于目标类是否实现接口 )。这个代理对象会在实际方法调用之前,调用事务管理器的begin()方法来开启事务;在方法正常执行结束后,调用事务管理器的commit()方法提交事务;如果方法执行过程中抛出异常,调用事务管理器的rollback()方法回滚事务。

3.传播行为与隔离级别处理:

@Transactional注解中可以指定事务传播行为(如REQUIRED、REQUIRES_NEW等)和隔离级别(如READ_COMMITTED、SERIALIZABLE等)。事务管理器会根据这些配置来正确处理事务之间的关系以及并发访问时的数据一致性问题。

事务失效场景

1.方法非public:@Transactional注解只能应用在public修饰的方法上。如果标注在private、protected或默认访问修饰符的方法上,事务不会生效。这是因为Spring的AOP代理机制在处理事务时,是基于Java的访问控制规则,非public方法无法被代理增强。

2.自调用问题:当一个类中的方法A调用同一类中的另一个被@Transactional标注的方法B时,事务不会生效。这是因为在这种情况下,没有经过代理对象调用,而是直接在目标对象内部调用,跳过了AOP的事务增强逻辑。例如:

typescript 复制代码
    @Service
    public class UserService {
        @Transactional
        public void methodA() {
            methodB();
        }
        @Transactional
        public void methodB() {
            // 业务逻辑
        }
    }

这里methodB的事务不会生效。

3. 未被Spring容器管理:如果一个类没有被Spring容器管理(没有使用@Component、@Service等注解将其纳入Spring容器),即使方法上标注了@Transactional,事务也不会生效,因为不存在Spring创建的代理对象来织入事务逻辑。

4. 异常被捕获处理:如果在被@Transactional标注的方法内部捕获了异常,并且没有重新抛出,事务不会回滚。例如:

typescript 复制代码
    @Service
    public class OrderService {
        @Transactional
        public void createOrder() {
            try {
                // 可能抛出异常的业务逻辑
                // 假设这里执行数据库插入操作出错
            } catch (Exception e) {
                // 捕获异常但未重新抛出
                e.printStackTrace();
            }
        }
    }

此时事务不会回滚,数据库操作会提交。

5.用的是mysql的MYISAM,这个引擎本身不支持事务。

6.rollbackFor没有设置对,比如默认没有任何配置,则方法内抛出IOException则不会回滚,需要配置@Transactional(rollbackFor=Exception.class)

7.@Transactional应用在final和static方法上,因为aop 默认是cglib代理(apringboot2.X开始),无法对final方法子类化。static是静态方法,属于类,不属于实例对象,无法被代理。

8.propagation传播机制配置错误。因为配置了Propagation.REQUIRES_NEW,是新启一个事务.因此2个事务之间是无法保证数据的一致性的。

java 复制代码
    public class OrderService {
    
        @Transactional(propagation=Propagation.REQUIRES_NEW)
            public void addOrderAndUser(int id) {
                try {
                    userMapper.addUser(id);
                    orderMapper.addOrder(id);
                    // 可能抛出异常的业务逻辑
                    // 假设这里执行数据库插入操作出错
                } catch (Exception e) {
                    // 捕获异常但未重新抛出
                    e.printStackTrace();
                }
            }
        @Transactional(propagation=Propagation.REQUIRES_NEW)
        public void addOrder(int id) {
            try {
                orderMapper.save(id);
                // 可能抛出异常的业务逻辑
                // 假设这里执行数据库插入操作出错
            } catch (Exception e) {
                // 捕获异常但未重新抛出
                e.printStackTrace();
            }
        }
    }
相关推荐
苏三的开发日记16 分钟前
windows系统搭建kafka环境
后端
爬山算法26 分钟前
Netty(19)Netty的性能优化手段有哪些?
java·后端
Tony Bai26 分钟前
Cloudflare 2025 年度报告发布——Go 语言再次“屠榜”API 领域,AI 流量激增!
开发语言·人工智能·后端·golang
想用offer打牌40 分钟前
虚拟内存与寻址方式解析(面试版)
java·后端·面试·系统架构
無量44 分钟前
AQS抽象队列同步器原理与应用
后端
努力学算法的蒟蒻1 小时前
day38(12.19)——leetcode面试经典150
算法·leetcode·面试
9号达人1 小时前
支付成功订单却没了?MyBatis连接池的坑我踩了
java·后端·面试
用户497357337982 小时前
【轻松掌握通信协议】C#的通信过程与协议实操 | 2024全新
后端
草莓熊Lotso2 小时前
C++11 核心精髓:类新功能、lambda与包装器实战
开发语言·c++·人工智能·经验分享·后端·nginx·asp.net
C雨后彩虹2 小时前
斗地主之顺子
java·数据结构·算法·华为·面试