JDK8源码分析Jdk动态代理底层原理

 本文侧重分析JDK8中jdk动态代理的源码,若是想看JDK17源码分析可以看我的这一篇文章 JDK17源码分析Jdk动态代理底层原理-CSDN博客

两者之间有着略微的差别,JDK17在JDK8上改进了不少

目录

源码分析 

过程

生成的代理类大致结构


 本文侧重分析JDK8中jdk动态代理的源码,若是想看JDK17源码分析可以看我的这一篇文章 JDK17源码分析Jdk动态代理底层原理-CSDN博客

两者之间有着略微的差别,JDK17在JDK8上改进了不少

      InvocationHandler最终存储在代理类实例的h字段中,这个字段是从Proxy类继承的。当调用代理对象的方法时,会调用这个h字段引用的InvocationHandler实例的invoke方法。这样就实现了方法调用的拦截和处理。 

源码分析 

public class Proxy implements java.io.Serializable {/** * InvocationHandler对象,用于处理代理类的方法调用* 这个字段会被所有代理类继承,存储在代理类实例中*/protected InvocationHandler h;/*** 代理类的构造函数,将InvocationHandler传递给代理类实例* 这个构造函数会被所有代理类调用*/protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}/*** 创建代理类实例的核心方法* loader: 类加载器,用于加载代理类* interfaces: 代理类需要实现的接口数组* h: 调用处理器,用于处理代理类的方法调用*/@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {// 检查InvocationHandler是否为空Objects.requireNonNull(h);// 复制接口数组,确保安全性final Class<?>[] intfs = interfaces.clone();// 获取安全管理器final SecurityManager sm = System.getSecurityManager();if (sm != null) {// 检查权限checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** 查找或生成代理类* 这是最核心的步骤,会生成代理类的字节码*/Class<?> cl = getProxyClass0(loader, intfs);try {// 检查权限if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}// 获取代理类的构造函数final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;// 如果构造函数是非public的,设置其可访问if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}// 创建代理类实例return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {throw new InternalError(e.toString(), e);} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}/*** 获取代理类的核心方法* 这个方法会调用ProxyClassFactory来生成代理类*/private static Class<?> getProxyClass0(ClassLoader loader,Class<?>[] interfaces) {// 如果接口数量超过65535,抛出异常if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// 从缓存中获取代理类,如果不存在则生成return proxyClassCache.get(loader, interfaces);}
}/*** 代理类工厂,负责生成代理类的字节码*/
private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>> {// 代理类名称前缀private static final String proxyClassNamePrefix = "$Proxy";// 原子计数器,用于生成唯一的代理类名private static final AtomicLong nextUniqueNumber = new AtomicLong();@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);// 验证接口for (Class<?> intf : interfaces) {// 验证类加载器能否加载这个接口validateInterface(intf, loader, interfaceSet);}// 生成代理类的名称String proxyPkg = null;     // 包名int accessFlags = Modifier.PUBLIC | Modifier.FINAL;// 检查所有非公共接口是否在同一个包中for (Class<?> intf : interfaces) {int flags = intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags = Modifier.FINAL;  // 移除public标志String pkg = intf.getPackageName();if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}// 如果没有指定包名,默认使用com.sun.proxyif (proxyPkg == null) {proxyPkg = "com.sun.proxy";}// 生成代理类的完整名称long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg.isEmpty()? proxyClassNamePrefix + num: proxyPkg + "." + proxyClassNamePrefix + num;// 生成代理类的字节码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {// 加载代理类return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());}}
}

过程

JDK动态代理生成代理对象的过程可以分为以下几个关键步骤:

1.调用Proxy.newProxyInstance方法,传入类加载器、接口数组和InvocationHandler实例。这是整个代理创建过程的入口。

2.通过getProxyClass0方法获取代理类。这个方法会首先检查缓存中是否已经存在对应的代理类,如果不存在则创建新的代理类。

3.ProxyClassFactory负责生成代理类的字节码,其中会,验证接口的合法性,生成唯一的代理类名,确定代理类的包名,调用ProxyGenerator生成字节码

4. 获取代理类的构造函数,这个构造函数接收一个InvocationHandler参数。

5.使用构造函数创建代理类实例,将InvocationHandler传入。这个InvocationHandler会被存储在代理类的h字段中。

生成的代理类大致结构

public final class $Proxy0 extends Proxy implements UserService {// 静态方法对象,用于方法调用private static Method m1;private static Method m2;private static Method m3;static {try {// 初始化方法对象m1 = Class.forName("java.lang.Object").getMethod("equals", Object.class);m2 = Class.forName("com.example.UserService").getMethod("addUser", String.class);// ... 其他方法} catch (NoSuchMethodException e) {throw new NoSuchMethodError(e.getMessage());}}// 构造函数,调用父类构造函数存储InvocationHandlerpublic $Proxy0(InvocationHandler h) {super(h);}// 实现接口方法@Overridepublic void addUser(String name) {try {// 调用InvocationHandler的invoke方法h.invoke(this, m2, new Object[]{name});} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}
}

       InvocationHandler最终存储在代理类实例的h字段中,这个字段是从Proxy类继承的。当调用代理对象的方法时,会调用这个h字段引用的InvocationHandler实例的invoke方法。这样就实现了方法调用的拦截和处理。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/65891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spire.PDF for .NET【页面设置】演示:向 PDF 添加平铺背景图像

平铺背景通常是指用一个或多个小图像重复填充的背景。在本文中&#xff0c;您将学习如何在 PDF 中平铺图像&#xff0c;并使用 C# 和 VB.NET 为您的 PDF 创建平铺背景。 Spire.PDF for .NET 是一款独立 PDF 控件&#xff0c;用于 .NET 程序中创建、编辑和操作 PDF 文档。使用 …

大带宽服务器和普通服务器相比较的优势

服务器作为各个企业线上业务中重要的网络设备&#xff0c;能够在网络中为其他客户机提供计算或者是应用服务&#xff0c;不同的应用场景中也会运用不同的服务器类型&#xff0c;本文就来为大家介绍一下大带宽服务器与普通服务器相比较来说的优势都有哪些&#xff01; 大带宽服务…

如何通过设置失效时间清除本地存储的数据

一、使用localStorage和时间戳&#xff08;JavaScript&#xff09; 1. 原理 localStorage是浏览器提供的一种在本地存储数据的方式&#xff0c;数据没有过期时间限制。但是可以通过自己记录时间戳来模拟数据过期的功能。在存储数据时&#xff0c;同时存储一个时间戳&#xff…

ImageNet 2.0?自动驾驶数据集迎来自动标注新时代

引言&#xff1a; 3DGS因其渲染速度快和高质量的新视角合成而备受关注。一些研究人员尝试将3DGS应用于驾驶场景的重建。然而&#xff0c;这些方法通常依赖于多种数据类型&#xff0c;如深度图、3D框和移动物体的轨迹。此外&#xff0c;合成图像缺乏标注也限制了其在下游任务中的…

stm32 智能语音电梯系统

做了个stm32智能语音控制的电梯模型&#xff0c;总结一下功能&#xff0c;源码用ST的HAL库写的&#xff0c;整体流程分明。 实物图 这个是整个板子的图片&#xff0c;逻辑其实并不复杂&#xff0c;只是功能比较多&#xff0c;在我看来都是一些冗余的功能&#xff0c;但也可能是…

多模态论文笔记——CogVLM和CogVLM2

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍多模态模型的LoRA版本——CogVLM和CogVLM2。在SD 3中使用其作为captioner基准模型的原因和优势。 文章目录 CogVLM论文背景VLMs 的任务与挑战现有方法及…

【react】Redux的设计思想与工作原理

Redux 的设计理念 Redux 的设计采用了 Facebook 提出的 Flux 数据处理理念 在 Flux 中通过建立一个公共集中数据仓库 Store 进行管理&#xff0c;整体分成四个部分即: View &#xff08;视图层&#xff09;、Action &#xff08;动作&#xff09;、Dispatcher (派发器)、Stor…

PCB层叠结构设计

PCB层叠结构设计 层叠结构设计不合理完整性相关案例&#xff1a;在构成回流路径时&#xff0c;由于反焊盘的存在&#xff0c;使高速信号回流路径增长&#xff0c;造成信号回流路径阻抗不连续&#xff0c;对信号质量造成影响。 PCB层叠结构实物&#xff1a;由Core 和 Prepreg&a…

爬虫在分析网站结构时的注意事项及代码示例

在进行网络爬虫的开发时&#xff0c;准确分析目标网站的结构是至关重要的一步。这不仅关系到爬虫的效率和效果&#xff0c;还涉及到是否能够合法合规地获取数据。本文将探讨在分析网站结构时需要注意的几个关键点&#xff0c;并提供相应的代码示例。 1. 网站的响应方式 首先&…

LLM训练的数据以及流程,怎么微调

LLM训练的数据以及流程,怎么微调 训练数据来源 互联网文本:从网页、新闻文章、博客、论坛等收集大量的文本内容。例如,Common Crawl项目会定期抓取大量的网页数据,为LLM训练提供了丰富多样的文本来源,这些数据涵盖了各种领域和主题,如科技、文化、娱乐、政治等.书籍文献…

【Cesium】七、设置Cesium 加载时的初始视角

文章目录 一、前言二、实现方法2.1 获取点位、视角2.2 设置 三、App.vue 一、前言 在前面的文章 【Cesium】三、实现开场动画效果 中有提到过 虽然也能回到初始点位但是有一个明显的动画过程。下面方法加载时就是在初始点位 没有动画效果&#xff0c;根据需求选择。 本文参考…

Edge安装问题,安装后出现:Could not find Edge installation

解决&#xff1a;需要再安装&#xff08;MicrosoftEdgeWebView2RuntimeInstallerX64&#xff09;。 网址&#xff1a;https://developer.microsoft.com/zh-cn/microsoft-edge/webview2/?formMA13LH#download 如果已经安装了edge&#xff0c;那就再下载中间这个独立程序安装就…

日期时间选择(设置禁用状态)

目录 1.element文档需要 2.禁用所有过去的时间 3.设置指定日期的禁用时间 <template><div class"block"><span class"demonstration">起始日期时刻为 12:00:00</span><el-date-pickerv-model"value1"type"dat…

【《python爬虫入门教程11--重剑无峰168》】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 【《python爬虫入门教程11--selenium的安装与使用》】 前言selenium就是一个可以实现python自动化的模块 一、Chrome的版本查找&#xff1f;-- 如果用edge也是类似的1.chrome…

grep -nr递归过滤文本时,如何忽略node_modules目录

在使用 grep -nr 递归过滤文本时&#xff0c;如果需要忽略 node_modules 目录&#xff0c;可以使用 --exclude-dir 参数&#xff0c;具体方法如下&#xff1a; 方法 1&#xff1a;使用 grep --exclude-dir grep -nr "your_search_text" --exclude-dirnode_modules .…

Spring Boot 3 文件下载、多文件下载以及大文件分片下载、文件流处理、批量操作 和 分片技术

在 Spring Boot 3 中&#xff0c;实现文件下载、多文件下载以及大文件分片下载需要结合以下功能&#xff1a;文件流处理、批量操作 和 分片技术。以下是详细实现方案&#xff1a; 1. 单文件下载 基础的单文件下载实现&#xff0c;可以参考以下代码&#xff1a; GetMapping(&…

系统架构风险、敏感点和权衡点的理解

系统架构是软件开发过程中的关键环节&#xff0c;它决定了系统的可扩展性、稳定性、安全性和其他关键质量属性。然而&#xff0c;架构设计并非易事&#xff0c;其中涉及的风险、敏感点和权衡点需要仔细考虑和处理。本文将详细探讨系统架构风险、敏感点和权衡点的概念&#xff0…

locate() 在MySQL中的用法

语法&#xff1a; 在MySQL中&#xff0c;LOCATE() 是一个字符串函数&#xff0c;用于返回一个子字符串在另一个字符串中第一次出现的位置。如果子字符串不存在&#xff0c;则返回0。这个函数的语法如下&#xff1a; LOCATE(substring, string[, start])substring&#xff1a;…

智能电话机器人优势是什么

在当今数字化转型加速的背景下&#xff0c;智能电话机器人&#xff08;IVR&#xff09;作为客户服务和业务流程优化的重要工具&#xff0c;正逐渐成为企业提升运营效率和服务质量的关键组成部分。 提高服务效率与响应速度 智能电话机器人能够自动处理大量重复性的查询和事务&…

leetcode热题100(79. 单词搜索)dfs回溯 c++

链接&#xff1a;79. 单词搜索 - 力扣&#xff08;LeetCode&#xff09; 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的…