当前位置: 首页 > 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;能够帮助像…...

OpCore Simplify技术突破:如何用智能适配重构开源系统定制效率

OpCore Simplify技术突破&#xff1a;如何用智能适配重构开源系统定制效率 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 在开源系统定制领域&#x…...

Pixel Epic动态卷轴技术揭秘:TextIteratorStreamer流式输出实现原理与调优

Pixel Epic动态卷轴技术揭秘&#xff1a;TextIteratorStreamer流式输出实现原理与调优 1. 引言&#xff1a;像素史诗的独特体验 Pixel Epic&#xff08;像素史诗&#xff09;作为一款研究报告辅助终端&#xff0c;最引人注目的特点莫过于其独特的"动态卷轴"输出效果…...

金蝶K3生产任务单状态查询SQL全解析:从计划到结案

1. 金蝶K3生产任务单状态查询SQL入门指南 第一次接触金蝶K3的生产任务单查询时&#xff0c;我也被那些复杂的SQL语句搞得头晕眼花。后来才发现&#xff0c;只要理解了系统设计逻辑&#xff0c;这些查询其实就像查快递单号一样简单。生产任务单在系统中会经历计划、确认、下达、…...

CodeT5安全使用指南:如何避免代码生成中的潜在风险

CodeT5安全使用指南&#xff1a;如何避免代码生成中的潜在风险 【免费下载链接】CodeT5 Home of CodeT5: Open Code LLMs for Code Understanding and Generation 项目地址: https://gitcode.com/gh_mirrors/co/CodeT5 CodeT5作为一款强大的代码理解与生成AI模型&#x…...

【优选算法篇】拓扑排序——逻辑先后与任务依赖的终极拆解

文章目录逻辑的枷锁&#xff1a;在依赖网中寻找出路零、 拓扑排序&#xff1a;打破逻辑混乱的“秩序之光”一、 课程表 I & II&#xff1a;经典拓扑排序 (Medium)1.1 题目描述1.2 算法思路&#xff1a;依赖关系的剥离1.3 C 代码实战 (以课程表 II 为例)二、 火星词典&#…...

Z-Image-Turbo在艺术创作中的实战:将文字灵感转化为超写实画作

Z-Image-Turbo在艺术创作中的实战&#xff1a;将文字灵感转化为超写实画作 你是否曾经有过绝妙的创意画面&#xff0c;却苦于无法将其具现化&#xff1f;Z-Image-Turbo极速云端创作室正是为解决这一痛点而生。这个基于先进AI技术的文生图工具&#xff0c;能够将你的文字描述在…...

NCCL中RoCE与RDMA的深度解析:如何优化分布式训练网络性能

1. 为什么RoCE和RDMA对分布式训练如此重要&#xff1f; 第一次接触分布式训练时&#xff0c;我盯着日志里不断跳动的通信耗时直发愁。8块GPU明明都在满负荷运转&#xff0c;但总训练时间就是比单卡8要长不少。后来用NVIDIA的Nsight工具一分析&#xff0c;发现超过30%的时间都花…...

为什么选择ODB++格式?Cadence与HyperLynx数据交换的最佳实践

为什么选择ODB格式&#xff1f;Cadence与HyperLynx数据交换的最佳实践 在高速PCB设计领域&#xff0c;数据格式的选择直接影响着设计到制造的整个流程效率。当工程师需要在Cadence Allegro和HyperLynx之间传递设计数据时&#xff0c;ODB正逐渐成为行业首选。这种智能数据格式不…...

优化算法中的‘0.618’魔法:黄金分割法为何是工程优化的首选入门工具?

黄金分割法&#xff1a;从古希腊美学到现代工程优化的优雅解决方案 在工程优化领域&#xff0c;算法选择往往让初学者感到困惑。面对梯度下降、牛顿法等复杂方法&#xff0c;有一种源自公元前300年的数学比例——黄金分割比&#xff08;0.618&#xff09;&#xff0c;却成为了…...

郭老师-我们为什么要爱国?

我们为什么要爱国&#xff1f; ——因为家在&#xff0c;根在&#xff0c;魂在“你可以不爱你的管家&#xff0c; 但必须爱你家的房子。”&#x1f33f; 国家如屋&#xff0c;人民为主&#xff0c; 执政者不过管家—— 而这屋&#xff0c;是我们的命脉所系。&#x1f3e0; 一、…...