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();
            }
        }
    }
相关推荐
猿java3 分钟前
Java String.replace()原理,你真的了解吗?
java·面试·架构
SimonKing12 分钟前
优雅地实现ChatGPT式的打字机效果:Spring流式响应
java·后端·程序员
xiaok14 分钟前
Nginx代理URL路径拼接问题(页面报404)
后端
咖啡Beans17 分钟前
干货:敏感数据实现加解密脱敏?Hutool的AES+hide一气呵成
后端
IT_陈寒2 小时前
Python开发者必知的5个高效技巧,让你的代码速度提升50%!
前端·人工智能·后端
大舔牛2 小时前
Viewport 与移动端 1px 问题解析
前端·面试
谦行2 小时前
Andrej Karpathy 谈持续探索最佳大语言模型辅助编程体验之路
后端
大舔牛2 小时前
Doctype作用? 严格模式与混杂模式如何区分? 它们有何意义?
前端·面试
ALex_zry3 小时前
Golang云端编程入门指南:前沿框架与技术全景解析
开发语言·后端·golang