简单模拟 Spring 创建的动态代理类(解释一种@Transactional事务失效的场景)
模拟 Spring 创建的动态代理类
本文主要目的是从父类和子类继承的角度去分析为什么在 @Service 标注的业务类中使用 this 调用方法会造成事务失效。解释在这种情况下 this 为什么是原始类对象而不是代理类对象。
问题描述
在 @Service 标注的业务类中,如果调用本类中的方法,那么会造成事务失效。原因是因为事务的功能是 @Transactional 注解通过 AOP 切面的方式对原始类进行的增强,因此事务功能是代理类对象中的方法才具备的。
现在问题来了,在 CGLib 的动态代理模式中,代理类(假设为 UserServiceImplProxy)是继承了 UserServiceImpl,也就是说代理类是原始类的子类,而通过 Spring 容器的 getBean 方法获取到的也是代理类对象,那么在主方法中调用 userServiceImplProxy.transactionFailTest() 方法,那问题似乎变成了在父类中使用 this 关键字时,this 代表的是子类对象还是父类对象?
先说结论,this 代表的对象是不确定的。
@Service
public class UserServiceImpl {@Autowiredprivate UserMapper userMapper;@Autowiredprivate UserServiceImpl userServiceImpl;public void transactionFailTest() {System.out.println("this=" + this);System.out.println("this.getClass()=" + this.getClass());System.out.println("this.getClass().getSuperclass() = " + this.getClass().getSuperclass());// 重点是探究this对象到底是什么?为什么this不是代理类对象this.transactionTest();}public void transactionSuccessTest() {// 调用代理类中的方法userServiceImpl.transactionTest();}@Transactionalpublic void transactionTest() {userMapper.updatePasswordById(1L, "111111");// if (true) {// throw new RuntimeException("故意制造异常");// }userMapper.updatePasswordById(2L, "222222");}
}
继承关系中的方法调用
在下面的测试案例中,同样是在父类 Parent 中的方法中使用 this 关键字,而实际调用的是子类 Child 中的方法。这是因为 main 方法中方法的调用者就是一个 Child 对象,所以无论是 Parent 类还是 Child 类中的 this,都是指向该调用对象的地址。
/*** 通过super调用父类方法*/
@Slf4j
public class SuperCallMainDemo {public static void main(String[] args) {Parent parent = new Child();log.error("main方法中的调用者对象={}", parent);parent.method01();}static class Child extends Parent {@Overridepublic void method01() {log.info("******************************************");super.method01();log.info("******************************************");}@Overridepublic void method02() {log.info("==========================================");super.method02();log.info("==========================================");}}static class Parent {public void method01() {log.info("Parent执行method01方法, this={}", this);this.method02();}public void method02() {log.info("Parent执行method02方法, this={}", this);}}
}

在继承中使用反射进行方法调用(模拟动态代理类逻辑)
在下面的测试案例中,和 Spring 通过 CGLIB 动态代理生成的动态代理类的原理相同。虽然代理类是子类,但由于是动态生成的,所以没有办法通过 super 关键字来直接调用父类中的同名方法,因此即使拦截到父类中的方法 m1、m2,也还是需要通过 invoke 反射的方式进行调用。因此 this 关键字指向的是 invoke 方法传递过去的父类对象。
/*** 通过反射调用父类方法*/
@Slf4j
public class InvokeCallMainDemo {public static void main(String[] args) {Parent parent = new Parent();log.error("main方法中parent的地址={}", parent);Parent child = new Child(parent, Parent.class);log.error("main方法中child的地址={}", child);child.method01();}static class Child extends Parent {Parent target;Class<?> clazz;Method m1;Method m2;@SneakyThrowspublic Child(Parent target, Class<?> clazz) {this.target = target;this.clazz = clazz;// 这里模拟代理类拦截父类的所有方法m1 = clazz.getMethod("method01");m2 = clazz.getMethod("method02");}@SneakyThrows@Overridepublic void method01() {log.info("******************************************");// 实际上这里的方法是被拦截下来的m1.invoke(target);log.info("******************************************");}@SneakyThrows@Overridepublic void method02() {log.info("==========================================");m2.invoke(target);log.info("==========================================");}}static class Parent {public void method01() {log.info("Parent执行method01方法, this={}", this);this.method02();}public void method02() {log.info("Parent执行method02方法, this={}", this);}}
}

总结
- 无论是那种调用方式,
this都表示实际调用的那个对象,不会因为使用super关键字而被更改。 - 在反射调用方式中,通过
method.invoke(target)进行调用方法时,传递的对象就是 target,因此this表示的就是 target 对象。(动态代理类只能选择这种方式) - Spring 中的代理类会保存原始类对象,通过反射的方式去调用原始类中的方法。这里通过模拟的方式实际上代理类中除了继承隐式地保存一个原始类对象之外,还显式地保存了一个原始类对象,因为 super 并不能够和 this 一样可以独立作为一个对象引用来使用。
相关文章:
简单模拟 Spring 创建的动态代理类(解释一种@Transactional事务失效的场景)
模拟 Spring 创建的动态代理类 本文主要目的是从父类和子类继承的角度去分析为什么在 Service 标注的业务类中使用 this 调用方法会造成事务失效。解释在这种情况下 this 为什么是原始类对象而不是代理类对象。 问题描述 在 Service 标注的业务类中,如果调用本类…...
万户OA upload任意文件上传漏洞复现
0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息…...
如何写好一篇软文?怎样写软文比较有吸引力?
软文,即柔性广告,是一种通过文字、图片等形式,将广告信息融入到内容中,以达到宣传、推广、营销目的的文章。企业和品牌每天都会在互联网上投放大量软文,软文起到润物细无声的作用,可以在无形中影响用户心智…...
从0开始学习JavaScript--JavaScript中的对象
JavaScript中的对象是一种重要的数据结构,它不仅是语言的基石,还提供了丰富的功能和灵活性。本文将深入研究JavaScript对象的创建、属性访问、方法定义,以及实际应用中的技巧,通过丰富的示例代码,帮助读者更全面地了解…...
【LeetCode刷题】--7.整数反转
7.整数反转 注意:在推入数字之前,需要判断MIN_VALUE< res*10digit<MAX_VALUE,不满足就返回0 class Solution {public int reverse(int x) {int res 0;while(x!0){//需要判断MIN_VALUE< res*10digit<MAX_VALUEif(res < Integ…...
Genio 500_MT8385安卓核心板:功能强大且高效
Genio 500(MT8385)安卓核心板是一款功能强大且高效的AIoT平台,内置的AI处理器(APU)工作频率可达500MHz,支持深度学习、神经网络加速和计算机视觉应用。配合高达2500万像素的摄像头,可以为AI相机应用提供清晰、精确的图像,如人脸识…...
idea导入javaweb变成灰色
解决办法: 如果这时候src是蓝色,其余都是灰色文件夹,这时候要先把src文件夹变成灰色,否则之后会报错 src文件变成灰色方法,右键src,选择make direcory as 选择unmark 如果src不是蓝色,就是灰色࿰…...
SpringBoot集成Memcached
SpringBoot集成Memcached 1、Memcached 介绍 Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中 缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储…...
git基本操作(配图超详细讲解)
个人主页:Lei宝啊 愿所有美好如期而遇 目录 创建git本地仓库 配置仓库 认识工作区,暂存区,版本库 修改文件 版本回退 撤销修改 删除文件 创建git本地仓库 要提前说的是,仓库是进⾏版本控制的⼀个⽂件⽬录。我们要想对⽂…...
【网络通信】浅析UDP与TCP协议的奥秘
在现代互联网中,UDP(用户数据报协议)和TCP(传输控制协议)是两种最常用的传输协议,它们被广泛应用于网络数据传输。尽管这两种协议都可以用来在网络上传输数据,但它们在设计目标、特点和适用场景…...
C#核心笔记——(二)C#语言基础
一、C#程序 1.1 基础程序 using System; //引入命名空间namespace CsharpTest //将以下类定义在CsharpTest命名空间中 {internal class TestProgram //定义TestProgram类{public void Test() { }//定义Test方法} }方法是C#中的诸多种类的函数之一。另一种函数*,还…...
C++ 删除无头链上所有指定值为x的节点。
C 删除无头链上所有指定值为x的节点。 #include<stdio.h> #include<ctype.h> #include<stdlib.h> typedef struct app {int data;struct app* next; }APP; int main() {int n;int node;int x;while (scanf("%d", &n) ! EOF){APP* head NULL, …...
linux基本指令以及热键
基本指令 ♥clear ♥whoami ♥who ♥pwd ♥ls指令(重点) ls -a: ls -l ♥mkdir ♥cd指令 ♥touch指令 ♥stat指令 ♥rmdir指令 && rm 指令 ♥man指令 ♥nano指令 ♥cp指令 ♥mv指令 ♥cat指令 🗡输出/输出重定向 …...
Rocketmq消费消息时不丢失不重复
消息消费不丢失 手动ACK 在消费者端,需要确保在消息拉取并消费成功之后再给Broker返回ACK,就可以保证消息不丢失了,如果这个过程中Broker一直没收到ACK,那么就可以重试。所以,在消费者的代码中,一定要在业…...
RedisInsight——redis的桌面UI工具使用实践
下载 官网下载安装。下载地址在这里 填个邮箱地址就可以下载了。 安装使用。 安装成功后开始使用。 1. 你可以add一个地址。或者登录redis cloud 去auto-discover 2 . 新增你的redis库地址。注意index的取值 3。现在可以登录到redis了。看看结果 这是现在 在服务器上执行…...
JVM对象创建与内存分配
对象的创建 对象创建的主要流程: 类加载推荐博客:JVM类加载机制详解 类加载检查 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析…...
央国企数字化转型难在哪?为什么要数字化转型?
科技在发展,技术在升级,全球信息化、数字化的步伐在加快,企业想要在未来的发展中抓住机会,更好地发展壮大,就需要加快企业数字化转型的速度,才能立足于信息化、数字化时代,央国企作为企业中的一…...
第7天:信息打点-资产泄漏amp;CMS识别amp;Git监控amp;SVNamp;DS_Storeamp;备份
第7天:信息打点-资产泄漏&CMS识别&Git监控&SVN&DS_Store&备份 知识点: 一、cms指纹识别获取方式 网上开源的程序,得到名字就可以搜索直接获取到源码。 cms在线识别: CMS识别:https://www.yun…...
不可思议,红警居然开源了!
红警,准确的说应该叫“红色警戒”,是大部分 80/90 后记忆里跟游戏二字关系最深的情节。 相信每一名 80/90 后,都有一段难忘的红警岁月,甚至可以说很多人的青春,就叫红警! 说到红色警戒游戏,估计应该是很多…...
yolo系列模型训练数据集全流程制作方法(附数据增强代码)
yolo系列的模型在目标检测领域里面受众非常广,也十分流行,但是在使用yolo进行目标检测训练的时候,往往要将VOC格式的数据集转化为yolo专属的数据集,而yolo的训练数据集制作方法呢,最常见的也是有两种,下面我…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
