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

Java中的动态代理(JDK Proxy VS CGLib)

前言

动态代理可以说是Java基础中一个比较重要的内容,这块内容关系到Spring框架中的AOP实现原理,所以特别写了一篇作为个人对这块知识的总结。这部分内容主要包括:JDK Proxy和CGLib的基本介绍、二者的实现原理、代码示例等。

什么是动态代理

动态代理是指程序在运行过程中,动态代理的实现是基于某个特点类而生成的增强类,也就是说,动态代理生成的类会顶替掉原来的类而被执行从而做到达到增强的目前

目前流行的动态代理机制有两种:JDK Proxy 和 CGLIB

这两个人实现动态代理的方式不同,前者是要求被代理类必须继承接口,而后者则没有这个要求,因为他是通过生成代理类的子类来进行构造的。

JDK Proxy

JDK Proxy 是Java官方提供的一种动态代理类实现方式,它的实现动态代理特点如下:

  • 代理类实现接口
  • 通过反射生产代理类

为什么被代理类要实现接口

可能有人会有疑惑为什么说JDK Proxy仅支持实现了接口的被代理类,这个问题需要分几点来回答

【首先】生成的代理类是通过反射的方式构建的,这个过程中,这个代理类可以认为就是被代理类的“克隆体”,但是与之不同的其中一个点的是,这个生产的代理类也需要继承Proxy才行,但是假如被代理类继承了其他类,但是Java中是不允许多继承的,所以被代理类是不能继承其他类,否则就会出现多继承的错误!!!

【其次】在Java中实现接口和继承类是不冲突的,为了在生产代理类后,我们可以拿到被代理类的同名方法然后进行重写(重写内容后续说明),所以JDK Proxy选择了实现接口的这种方式

代码示例

以下是动态代理的一个代码示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 定义接口
interface UserService {void save();
}// 实现接口的具体类
class UserServiceImpl implements UserService {public void save() {System.out.println("Save user");}
}// 实现InvocationHandler接口来自定义代理逻辑
class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method");Object result = method.invoke(target, args);System.out.println("After method");return result;}
}public class Main {public static void main(String[] args) {UserService userService = new UserServiceImpl();// 创建InvocationHandler对象MyInvocationHandler handler = new MyInvocationHandler(userService);// 通过Proxy类的静态方法创建代理对象UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),handler);// 调用代理对象的方法proxy.save();}
}

在这段代码中

【1】我们先看接口和被代理类,实现比较简单,接口实现类重写接口方法。

【2】MyInvocationHandler是一个接口实现类,它的目的是为了创建自定义的代理规则逻辑,target属性就是被代理类,MyInvocationHandler的构造方法必须将被代理对象的引用传递进来,然后进行属性赋值

【3】MyInvocationHandler中的invoke()方法是最核心的,它才是实现动态代理规则逻辑的主要部分

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method");Object result = method.invoke(target, args);System.out.println("After method");return result	
}

在这个方法中,method.invoke这个语句就是我们被代理类的方法执行,这个方法中通过传递被代理类target,在内部调用这个被代理类的方法,然后在metho.invoke方法上下就可以执行其他前置后置增强逻辑,也就是说,我们生成的动态代理类的同名方法是通过实现前置逻辑和后置逻辑+被代理类的同名方法来实现动态代理,这样既不会丢失原本逻辑也可以做大增强!!!

【4】在main方法中

proxy.save()

这个方法其实即是动态代理生成的方法,也就是MyInvocationHandler中的invoke方法

图解(逻辑)

在这里插入图片描述

CGLIB

CGLIB是非官方创建的动态代理库。它通过继承被代理类来创建代理类。CGLib不需要被代理类实现接口,它能够代理普通的类。CGLib通过修改字节码来实现代理。

代码示例

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 被代理类
class RealSubject {public void doSomething() {System.out.println("RealSubject doSomething");}
}// 代理类
class ProxySubject implements MethodInterceptor {private Object target; // 被代理对象public Object getInstance(Object target) {this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass());enhancer.setCallback(this);return enhancer.create();}public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method: " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("After method: " + method.getName());return result;}
}public class CGLIBDynamicProxyExample {public static void main(String[] args) {ProxySubject proxy = new ProxySubject();RealSubject realSubject = (RealSubject) proxy.getInstance(new RealSubject());realSubject.doSomething();}
}

【1】在这段代码中,我们会发现在ProxySubject类中的getInstance方法也需要传入被代理类target,但与JDK 不同的是,这个方法内部是为了获取他的class类,然后生成一个代理类继承自被代理类。

【2】与JDK Proxy中类似,也有一个类似于invoke方法的intercept方法,他内部执行的proxy.invokeSuper(obj,args),其实就是调用父类的目标方法,也就是被代理类的方法

【3】在main方法中,我们会发现其创建代理类的方式和JDK Proxy很相似,都需要把代理规则类放入到代理工厂中进行创建,最后创建出来的对象就是被代理对象的子类

图解

在这里插入图片描述

仅为个人学习笔记
感谢阅读

相关文章:

Java中的动态代理(JDK Proxy VS CGLib)

前言 动态代理可以说是Java基础中一个比较重要的内容,这块内容关系到Spring框架中的AOP实现原理,所以特别写了一篇作为个人对这块知识的总结。这部分内容主要包括:JDK Proxy和CGLib的基本介绍、二者的实现原理、代码示例等。 什么是动态代理…...

Redis 7 第七讲 哨兵模式(sentinal)

哨兵模式 哨兵巡查监控后台master主机是否故障,如果出现故障根据投票时自动将某一个从库转换成新的主库,继续对外服务。 作用 1. 监控redis运行状态,包括master和slave 2. 当master down机,能自动将salve切换成新的master 应用场景 主从监控监控主从redis库运行的状态…...

Python入门教程 - 判断语句(二)

目录 一、布尔类型 二、比较运算符 三、if判断语句 一、布尔类型 True False result1 10 > 5 result2 10 < 5 print(result1) print(result2) print(type(result1)) True False <class bool> 二、比较运算符 ! > < > < 比较运算的结果是布尔…...

LeetCode-55-跳跃游戏-贪心

题目描述&#xff1a; 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 解…...

【USRP】调制解调系列4:BPSK、QPSK、8PSK、OQPSK、Pi/4DQPSK,基于labview的实现

PSK Phase Shift Keying – 相移键控 在某些调制解调器中用于数据传输的调制系统&#xff0c;在最简单的方式中&#xff0c;二进制调制信号产生0和1。载波相位来表示信号占和空或者二进制1和O。对于有线线路上较高的数据传输速率&#xff0c;可能发生4个或8个不同的相移&…...

深入探讨梯度下降:优化机器学习的关键步骤(一)

文章目录 &#x1f340;引言&#x1f340;什么是梯度下降&#xff1f;&#x1f340;损失函数&#x1f340;梯度(gradient)&#x1f340;梯度下降的工作原理&#x1f340;梯度下降的变种&#x1f340;随机梯度下降&#xff08;SGD&#xff09;&#x1f340;批量梯度下降&#xf…...

layui框架学习(40:数据表格_主要事件)

Layui数据表格模块主要通过各类事件响应工具栏操作、单元格编辑或点击等交互操作&#xff0c;本文学习table数据表格模块中的主要事件及处理方式。   头部工具栏事件。通过代码“table.on(‘toolbar(test)’, function(obj))”获取lay-filter属性为test的数据表格的头部工具栏…...

kotlin实现猜数游戏

游戏规则 1.程序随机生成一个1到100的数字&#xff0c;作为MagicNumber 2.用户根据提示输入数据&#xff0c;只有三次机会输入数据 代码 代码很简单&#xff0c;使用了let内置函数 fun main() {//生成随机数可以使用java的方法//val magicNumber Random().nextInt(11)val ma…...

51单片机项目(8)——基于51单片机的DS1302时钟系统

本次做的设计&#xff0c;是利用DS1302时钟芯片&#xff0c;做了一个时钟&#xff0c;并且将实时时间通过串口发送到上位机进行显示。系统运行如下&#xff1a;&#xff08;protues文件和相关keil代码会在文章最后给出&#xff01;&#xff01;&#xff01;&#xff09; DS1302…...

高频策略:做市商与逆向选择

参与交易市场的三类人&#xff1a; 出于某种现实的需要而进行交易的人。例如投资者买入股票等金融资产长期持有&#xff0c;是为了使自己当前的资产进行升值&#xff0c;获得比银行利息更高的收益&#xff1b;制造业公司为了锁定生产成本而进行对冲交易。对于这些人来说&#…...

Valgrind内存诊断工具的使用笔记

Valgrind是一款用于内存调试、内存泄漏检测以及性能分析的神器&#xff0c;能够帮助我们快速定位到程序的内存问题&#xff0c;比如内存泄漏导致的 段错误 (核心已转储) 包含以下工具&#xff1a; Memcheck&#xff1a;检查程序中的内存问题&#xff0c;如泄漏、越界、非法指…...

docker安装Nacos

哈喽&#xff01;大家好&#xff0c;我是旷世奇才李先生 文章持续更新&#xff0c;可以微信搜索【小奇JAVA面试】第一时间阅读&#xff0c;回复【资料】更有我为大家准备的福利哟&#xff0c;回复【项目】获取我为大家准备的项目 文章目录 一、Nacos是什么1、简介2、功能1、服务…...

【Linux】线程安全-死锁

文章目录 死锁问题场景1场景2死锁的gdb调试造成死锁的必要条件不可剥夺循环等待互斥条件请求和保持 预防死锁破坏必要条件&#xff0c;循环等待&请求和保持加锁顺序一致避免锁没有被释放资源一次性分配 死锁问题 死锁的两种场景&#xff1a; 场景1 线程加锁之后一直没有将锁…...

pdf转换成图片免费软件用哪个?pdf转换成图片就用它

随着技术的发展&#xff0c;现在企业办公运用到的电子文档各种各样&#xff0c;我们日常需要掌握的技能越来越高要求&#xff0c;其中pdf和图片是我们经常接触的文件格式之一&#xff0c;而且这两个文件格式我们会经常将它们进行转换&#xff0c;那么pdf转换成图片怎么操作呢?…...

【LeetCode】《LeetCode 101》第十二章:字符串

文章目录 12.1 字符串比较242 . 有效的字母异位词&#xff08;简单&#xff09;205. 同构字符串&#xff08;简单&#xff09;647. 回文子串&#xff08;中等&#xff09;696 . 计数二进制子串&#xff08;简单&#xff09; 12.2 字符串理解224. 基本计算器&#xff08;困难&am…...

Android去掉视频声音

【Android】使用MediaExtractor、MediaMuxer去掉视频文件中的音频数据_android 去掉视频音频_little_fat_sheep的博客-CSDN博客 void removeSound() {try {String path Environment.getExternalStorageDirectory().getPath();String filename "no_sound_" input_p…...

java-thread-affinity线程绑核

通过将线程绑定到指定的cpu上&#xff0c;可以提高执行效率。因为每次都是相同的cpu,可以充分利用高速缓存&#xff0c;在java中可以使用以下依赖来使用。 <dependency><groupId>net.openhft</groupId><artifactId>affinity</artifactId><ver…...

Springboot - 5.test集成

&#x1f440;1. 简介 spring-boot-starter-test是Spring Boot框架中的一个模块&#xff0c;用于支持在项目中进行单元测试和集成测试。它提供了一些依赖项和工具&#xff0c;使得编写和运行测试变得更加方便。以下是关于spring-boot-starter-test的全面介绍&#xff1a; ✌1…...

弯道超车必做好题集锦三(C语言编程题)

目录 前言&#xff1a; 1.单词倒排 方法1&#xff1a;scanf匹配特定字符法 方法2&#xff1a; 双指针法 2.统计每个月兔子的总数 方法1&#xff1a;斐波那契数列 方法2&#xff1a;斐波那契的递归 3.珠玑妙算 方法&#xff1a;遍历 4.寻找奇数&#xff08;单身狗&#…...

JavaScript基础语法03——JS注释、结束符

哈喽&#xff0c;大家好&#xff0c;我是雷工&#xff01; 今天继续学习JavaScript基础语法知识&#xff0c;注释和结束符&#xff0c;以下为学习笔记。 一、JavaScript注释 JavaScript注释有什么作用&#xff1f; JavaScript注释可以提高代码的可读性&#xff0c;能够帮助像…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...