一、原理

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{@Overridepublic void verifyOrderParameters() {System.out.println("校验订单参数");// 调用保存订单方法saveOrder();}@Override@Transactionalpublic void saveOrder() {System.out.println("保存订单信息到DB");}
}

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

OrderServiceImplProxy

/*** 订单业务层具体处理类的代理类*/
public class OrderServiceImplProxy implements OrderService{/*** 持有被代理的具体的目标对象*/private OrderServiceImpl orderServiceImpl;public OrderServiceImplProxy(OrderServiceImpl orderServiceImpl) {this.orderServiceImpl = orderServiceImpl;}@Overridepublic void verifyOrderParameters() {orderServiceImpl.verifyOrderParameters();}@Overridepublic 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();
}@Transactionalpublic void insertOrder() {//SQL操作}
}

变更为:

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

@Transactional类内部访问失效原因详解相关推荐

  1. C++中空类占一字节原因详解

    C++中空类占一字节原因详解 - XSH的博客 - CSDN博客 https://blog.csdn.net/xiong452980729/article/details/71077144 在C++中 ...

  2. C++友元函数和友元类(C++ friend)详解

    在看VISP视觉库的时候遇到友元函数: Friends void swap (vpDetectorAprilTag &o1, vpDetectorAprilTag &o2) 在定义一个 ...

  3. Java类的加载过程详解 面试高频!!!值得收藏!!!

    受多种情况的影响,又开始看JVM 方面的知识. 1.Java 实在过于内卷,没法不往深了学. 2.面试题问的多,被迫学习. 3.纯粹的好奇. 很喜欢一句话: 八小时内谋生活,八小时外谋发展. 望别日与 ...

  4. 中yeti不能加载_第二十章_类的加载过程详解

    类的加载过程详解 概述 在 Java 中数据类型分为基本数据类型和引用数据类型.基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载 按照 Java 虚拟机规范,从 Class 文件到加载到内 ...

  5. java类修饰词和内部类详解

    java类修饰词和内部类详解 控制属性: 同一类内     同一包内      子类     所有类 public            可             可         可       ...

  6. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(一)

    文章大纲 引言 一.Android Storage Access Framework 二.Storage Access Framework 的主要角色成员 1.Document Provider 文件 ...

  7. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(二)

    文章大纲 引言 一.DirectFragment 1.当选中DirectoryFragment中RecyclerView的Item时 2.选中DirectoryFragment中RecyclerVie ...

  8. java访问修饰符详解——学java,零基础不怕,不只要理论,更要实践+项目,a href=http://www.bjweixin.com太原维信科技提供 /a...

    java访问修饰符详解--学java,零基础不怕,不只要理论,更要实践+项目 <a href=http://www.bjweixin.com>太原维信科技提供 </a> pub ...

  9. colsure php_PHP_PHP中Closure类的使用方法及详解,Closure,匿名函数,又称为Anonym - phpStudy...

    PHP中Closure类的使用方法及详解 Closure,匿名函数,又称为Anonymous functions,是php5.3的时候引入的.匿名函数就是没有定义名字的函数.这点牢牢记住就能理解匿名函 ...

最新文章

  1. EBGAN, LSGAN, BEGAN
  2. ubunto安装mysql8.13_ubuntu安装mysql[ubuntu部署]
  3. 企业网络推广“卡壳”了?如何更有效提升SEO排名?
  4. (三)html5的结构
  5. CVTE2016校招试题摘选
  6. [Java基础]哈希值
  7. [蓝桥杯][算法提高VIP]分分钟的碎碎念-dfs
  8. 李开复:21世纪7种人才最抢手
  9. [Diary]6.10
  10. 研讨会 | 知识图谱大咖云集阿里,他们都说了啥
  11. map的四种遍历方式
  12. 日本研发投篮机器人Cue,投球命中率接近100%
  13. 《Java程序员职场全攻略:从小工到专家》连载十:这条路大家都是怎么走的
  14. autosar架构详细介绍_干货|非常详细的 Ceph 介绍、原理、架构
  15. CAS132172-61-3阳离子脂质体DOTAP科研用
  16. 菜鸟、小白在autojs和冰狐智能辅助之间如何选择?
  17. Unsafe code may only appear if compiling with /unsafe
  18. GIS 通过osm2po离线最短路径和路径规划后台计算
  19. microsoft excel使用技巧和问题解决
  20. 数据库—属性 码 候选码 主码 外码 主属性 非主属性

热门文章

  1. (学习日记)2023.04.25
  2. 为何汽车制造商需要关注HTML5
  3. 关闭远程,模拟鼠标键盘失效!
  4. 先装Vista后装XP双系统的方法
  5. javaweb基于JSP+Servlet开发北京市地铁(火车票)订票系统+论文+PPT+任务书+检查表 毕业设计
  6. html中的样式写在什么位置,css样式放在哪里?
  7. php高强度精密涂覆钢管,双面涂塑钢管需求和发展 矿用涂塑复合钢管使用常识...
  8. 去耦、旁路、滤波电容的区别
  9. 用十年教会自己编程_什么学习编程实际上教会了我
  10. 微信红包程序c语言,C语言实战番外篇——模拟微信抢红包