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();
            }
        }
    }
相关推荐
程序员爱钓鱼11 分钟前
Python编程实战 | 函数与模块化编程 - 第三方库的安装与管理(pip使用)
后端·python·ipython
程序员爱钓鱼13 分钟前
Python编程实战 | 面向对象与进阶语法-类与对象的概念
后端·python·ipython
IT_陈寒1 小时前
Redis性能翻倍的5个冷门技巧,90%的开发者都不知道第3个!
前端·人工智能·后端
噔噔噔噔@1 小时前
软件测试面试的排序算法问题如何回答
面试·职场和发展·排序算法
雨中散步撒哈拉1 小时前
14、做中学 | 初二上期 Golang集合Map
开发语言·后端·golang
陈老师还在写代码1 小时前
springboot 打包出来的 jar 包的名字是在哪儿决定的
spring boot·后端·jar
꒰ঌ 安卓开发໒꒱9 小时前
RabbitMQ面试全解析:从核心概念到高可用架构
面试·架构·rabbitmq
熊小猿10 小时前
在 Spring Boot 项目中使用分页插件的两种常见方式
java·spring boot·后端
paopaokaka_luck10 小时前
基于SpringBoot+Vue的助农扶贫平台(AI问答、WebSocket实时聊天、快递物流API、协同过滤算法、Echarts图形化分析、分享链接到微博)
java·vue.js·spring boot·后端·websocket·spring
杨筱毅10 小时前
【C++】【常见面试题】最简版带大小和超时限制的LRU缓存实现
c++·面试