@Transactional 自调用失效问题解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: @Transactional 自调用失效问题解析

一、背景

 

”脏脏包“在技术群里问了一个问题:”大家有在项目中遇到这样的场景吗 在一个service层重写的方法中调用一个私有方法。 service重写的方法不加事务 私有方法想加入事务 他去调用私有方法时 私有方法需要被事务控制“ 。

这个问题比较典型,面试时也经常被问到,在此简单整理一下。

二、Spring注解方式的事务实现机制

在应用调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器(图 2 有相关介绍)AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务, 如图 1 所示。

image.png

图 1. Spring 事务实现机制

Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,图 1 是以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。

对于 JdkDynamicAopProxy,需要调用其 invoke 方法。

三、分析

3.1 为什么@Transactional 只能应用到 public 方法才有效?

3.1.1 从理论角度
理论上@Transactional 注解是为了进行事务增强。

JDK 动态代理,比如需事先接口才行,因此必然是 public的。

AspectJ 动态代理,是基于类的代理,如果该类的方法为私有,那么子类中就无法重写和调用。

3.1.2 从源码角度
这是因为在使用 Spring AOP 代理时,Spring 在调用在的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取 @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。

其源码如下:

private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// 关键在这里

        if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        } else {
            Class<?> userClass = ProxyUtils.getUserClass(targetClass);
            Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
            specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
            TransactionAttribute txAtt = null;
            if (specificMethod != method) {
                txAtt = this.findTransactionAttribute(method);
                if (txAtt != null) {
                    return txAtt;
                }

                txAtt = this.findTransactionAttribute(method.getDeclaringClass());
                if (txAtt != null || !this.enableDefaultTransactions) {
                    return txAtt;
                }
            }

            txAtt = this.findTransactionAttribute(specificMethod);
            if (txAtt != null) {
                return txAtt;
            } else {
                txAtt = this.findTransactionAttribute(specificMethod.getDeclaringClass());
                if (txAtt != null) {
                    return txAtt;
                } else if (!this.enableDefaultTransactions) {
                    return null;
                } else {
                    Method targetClassMethod = this.repositoryInformation.getTargetClassMethod(method);
                    if (targetClassMethod.equals(method)) {
                        return null;
                    } else {
                        txAtt = this.findTransactionAttribute(targetClassMethod);
                        if (txAtt != null) {
                            return txAtt;
                        } else {
                            txAtt = this.findTransactionAttribute(targetClassMethod.getDeclaringClass());
                            return txAtt != null ? txAtt : null;
                        }
                    }
                }
            }
        }
    }

非公有函数事务属性信息返回null

3.2 为什么自调用无效?

在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。

若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。

@Service
public class OrderService {

private void insert() {

insertOrder();
}

@Transactional

public void insertOrder() {
    //SQL操作
   }

}
insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。

四、解决方法

4.1 可以使用ApplicatonContextHolder 工具类,从上下文中获取当前bean,再调用。

4.2 可以使用上下文工具类获取当前对象的代理类  @EnableAspectJAutoProxy (exposeProxy = true) 然后通过下面方法获取代理对象,然后再调用

@Service
public class OrderService {

public void insert() {

OrderService proxy = (OrderService) AopContext.currentProxy();
   proxy.insertOrder();
}

@Transactional
public void insertOrder() {
    //SQL操作
   }

}
4.3  maven中加入spring-aspects 和 aspectjrt 的依赖以及 aspectj-maven-plugin插件

注:

第二节和第三节部分内容转载自:

 透彻的掌握 Spring 中@transactional 的使用

创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

相关文章
|
7月前
|
Java 开发者 Spring
深入解析 @Transactional:Spring 事务管理的艺术及实战应对策略
深入解析 @Transactional:Spring 事务管理的艺术及实战应对策略
|
8月前
|
XML Java 数据库连接
无痛事务管理:Spring中的@Transactional和相关注解完全解析
无痛事务管理:Spring中的@Transactional和相关注解完全解析
1348 0
|
Java 数据库 Spring
深入理解 Java 中的 @Transactional 注解:事务管理的精髓解析
在现代软件开发中,数据库事务管理是确保数据一致性和完整性的关键部分。Java 中的 `@Transactional` 注解是实现事务管理的重要工具之一。通过该注解,我们可以轻松地声明方法或类的事务行为,实现数据库操作的原子性和隔离性。本文将带您深入探索 Java 中的 `@Transactional` 注解,揭示其作用、用法以及在实际开发中的应用场景。
|
SQL Java Maven
@Transactional 自调用失效问题解析
@Transactional 自调用失效问题解析
454 0
|
存储 XML 前端开发
Spring 5 中文解析数据存储篇-@Transactional使用
本章节主要描述:Spring 5 中文解析数据存储篇-@Transactional使用。
670 0
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
96 2
|
3月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
89 0
|
3月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
69 0
|
17天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
17天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析

推荐镜像

更多