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();
            }
        }
    }
相关推荐
bobz9652 分钟前
calico vs cilium
后端
绝无仅有34 分钟前
面试实战总结:数据结构与算法面试常见问题解析
后端·面试·github
绝无仅有37 分钟前
Docker 面试常见问题及解答
后端·面试·github
程序员爱钓鱼40 分钟前
Go语言100个实战案例-项目实战篇:股票行情数据爬虫
后端·go·trae
IT_陈寒1 小时前
Redis 性能翻倍的 7 个冷门技巧,第 5 个大多数人都不知道!
前端·人工智能·后端
你的人类朋友9 小时前
说说签名与验签
后端
databook9 小时前
Manim实现脉冲闪烁特效
后端·python·动效
倔强青铜三11 小时前
苦练Python第46天:文件写入与上下文管理器
人工智能·python·面试
canonical_entropy13 小时前
AI时代,我们还需要低代码吗?—— 一场关于模型、演化与软件未来的深度问答
后端·低代码·aigc
颜如玉14 小时前
HikariCP:Dead code elimination优化
后端·性能优化·源码