当前位置: 首页 > news >正文

@Transactional类内部访问失效原因详解

一、原理

Spring之所以可以对开启@Transactional的方法进行事务管理,是因为Spring为当前类生成了一个代理类,然后在执行相关方法时,会判断这个方法有没有@Transactional注解,如果有的话,则会开启一个事务。

也就是说我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。(该部分摘自:事务注解Transactional在同一个类中调用的失效问题_fastjson_的博客-CSDN博客)

Spring在扫描bean的时候,如果扫描到方法上有这些注解,那么spring会通过动态代理模式,为这个bean动态地生成一个代理类,在代理类中,会对有注解的这个方法,做一些增强处理,如给有@Transactional注解的方法开启transaction。

当我们想要调用这个方法时,实际上是先调用了代理对象中被增强的方法,然后在代理对象中,又会调用我们实际的目标对象中的方法。在通过代理对象中转的这一过程中,像上边说的开启和提交transaction就实现了。(该部分摘自:@transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!…_weixin_39738667的博客-CSDN博客)

二、代码模拟

实现机制我们大体知道了,下面我们用代码来模拟演示一下。

假设我们订单业务处理类中,有两个方法:校验订单参数方法verifyOrderParameters() 和 保存订单方法saveOrder(),其中saveOrder方法上加了@Transactional注解,verifyOrderParameters方法内会调用saveOrder方法。

OrderService

/**
 * 订单业务层的接口定义
 */
public interface OrderService {
 
    /**
     * 校验订单参数
     */
    void verifyOrderParameters();
 
    /**
     * 保存订单
     */
    void saveOrder();
}

OrderServiceImpl

/**
 * 订单业务层的具体处理类
 */
public class OrderServiceImpl implements OrderService{
 
    @Override
    public void verifyOrderParameters() {
        System.out.println("校验订单参数");
        // 调用保存订单方法
        saveOrder();
    }
 
    @Override
    @Transactional
    public void saveOrder() {
        System.out.println("保存订单信息到DB");
    }
}

我们用伪代码来演示一下Spring动态代理生成的代理类。

OrderServiceImplProxy

/**
 * 订单业务层具体处理类的代理类
 */
public class OrderServiceImplProxy implements OrderService{
 
    /**
     * 持有被代理的具体的目标对象
     */
    private OrderServiceImpl orderServiceImpl;
 
    public OrderServiceImplProxy(OrderServiceImpl orderServiceImpl) {
        this.orderServiceImpl = orderServiceImpl;
    }
 
    @Override
    public void verifyOrderParameters() {
        orderServiceImpl.verifyOrderParameters();
    }
 
    @Override
    public void saveOrder() {
        System.out.println("开启事务。。。");
        orderServiceImpl.saveOrder();
        System.out.println("提交事务。。。");
    }
}

这边我们来看下客户端调用saveOrder方法和verifyOrderParameters方法,输出结果都是什么样的。

public class Client {
 
    public static void main(String[] args) {
        // 创建一个订单业务的真实处理对象
        OrderServiceImpl orderServiceImpl = new OrderServiceImpl();
        // 创建一个代理对象
        OrderServiceImplProxy orderServiceImplProxy = new OrderServiceImplProxy(orderServiceImpl);
        // 执行代理对象的校验订单方法
        orderServiceImplProxy.verifyOrderParameters();
        System.out.println("--------------------------------------------");
        // 执行代理对象的保存订单方法
        orderServiceImplProxy.saveOrder();
    }
}

执行main方法后,得到下边的输出:

校验订单参数
保存订单信息到DB
--------------------------------------------
开启事务。。。
保存订单信息到DB
提交事务。。。

所以,普通方法verifyOrderParameters内调用注解方法saveOrder时,其实调用的是原目标对象(orderServiceImpl)的saveOrder方法,没有走代理对象(orderServiceImplProxy)中被增强的saveOrder方法,所以就不会产生效果啦。

该部分摘自:@transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!…_weixin_39738667的博客-CSDN博客

三、解决方案

方法1:

将事务方法放到另一个类中进行调用。

方法2:

获取本对象的代理对象,再进行调用。具体操作如:

@Service
public class OrderService {
 
    private void insert() {
insertOrder();
}
 
@Transactional
    public void insertOrder() {
        //SQL操作
       }
}

变更为:

 @Service
public class OrderService {
 
  public void insert() {
 
    OrderService proxy = (OrderService) AopContext.currentProxy();
       proxy.insertOrder();
    }
 
    @Transactional
    public void insertOrder() {
        //SQL操作
       }
}

最后

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

小编已加密:aHR0cHM6Ly9kb2NzLnFxLmNvbS9kb2MvRFVrVm9aSGxQZUVsTlkwUnc==出于安全原因,我们把网站通过base64编码了,大家可以通过base64解码把网址获取下来。

相关文章:

  • java计算机毕业设计基于安卓Android微信的学生家庭个人支出管理小程序 uniAPP
  • 2022年最新Vue+electron项目创建
  • 1.2继承性
  • springboot+vue 课程在线考试系统 java
  • 【网络服务数据库教程】06 Web服务器 - Nginx 一键安装搭建Wordpress博客系统
  • Electron开发环境准备
  • 驻波在物理上的应用与魅力
  • 【网络服务数据库教程】08 邮件服务
  • python 经典案例(3)
  • [Python从零到壹] 五十三.图像增强及运算篇之直方图均衡化处理
  • [ vulhub漏洞复现篇 ] AppWeb认证绕过漏洞(CVE-2018-8715)
  • 二十三.基于国民MCU 的MCO模块的分析
  • php计算机毕业设计基于thinkphp框架的特色旅游网站vue
  • 开学季:好好聊聊自己的大学生活
  • java评论、回复功能设计和实现
  • 时间复杂度分析经典问题——最大子序列和
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • es6--symbol
  • Java教程_软件开发基础
  • PHP面试之三:MySQL数据库
  • Puppeteer:浏览器控制器
  • SpriteKit 技巧之添加背景图片
  • 测试如何在敏捷团队中工作?
  • 机器学习 vs. 深度学习
  • 前端路由实现-history
  • 微服务框架lagom
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 问题之ssh中Host key verification failed的解决
  • 我的zsh配置, 2019最新方案
  • 一、python与pycharm的安装
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • #、%和$符号在OGNL表达式中经常出现
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • (26)4.7 字符函数和字符串函数
  • (LeetCode) T14. Longest Common Prefix
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (十八)三元表达式和列表解析
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • ../depcomp: line 571: exec: g++: not found
  • .bat批处理(六):替换字符串中匹配的子串
  • .Mobi域名介绍
  • .NET Core Web APi类库如何内嵌运行?
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • .net mvc部分视图
  • .NET/C# 使窗口永不获得焦点
  • .pop ----remove 删除
  • .py文件应该怎样打开?
  • @CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思
  • [ 数据结构 - C++] AVL树原理及实现
  • [2018/11/18] Java数据结构(2) 简单排序 冒泡排序 选择排序 插入排序
  • [Android] Implementation vs API dependency
  • [C/C++]数据结构 深入挖掘环形链表问题