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 文档。使用 …

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…

【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…

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

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

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

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

用PicGo向Github图床上传图片,然后通过markdown语言显示图片

目录 下载PicGo软件图床GitHub设置在Markdown中使用图片 下载PicGo软件 先进入Pic官网&#xff0c;然后点击下图中的免费下载 然后点击下载下图中PicGo-Setup-2.4.0-beta.9.exe这个可执行软件 图床GitHub设置 点击PicGo中的图床设置&#xff0c;再点击其中的Github&#xff…

bilibili 哔哩哔哩小游戏SDK接入

小游戏的文档 简介 bilibili小游戏bilibili小游戏具有便捷、轻量、免安装的特点。游戏包由云端托管&#xff0c;在哔哩哔哩APP内投放和运行&#xff0c;体验流畅&#xff0c;安全可靠。https://miniapp.bilibili.com/small-game-doc/guide/intro/ 没想过接入这个sdk比ios还难…

Spring Cloud Alibaba2022之Sentinel总结

Spring Cloud Alibaba2022之Sentinel学习 Sentinel介绍 Sentinel是一个面向云原生微服务的流量控制、熔断降级组件。 Sentinel 分为两个部分&#xff1a; 核心库&#xff1a;&#xff08;Java 客户端&#xff09;不依赖任何框架/库&#xff0c;能够运行于所有 Java运行时环 …

HarmonyOS:删除多层ForEach循环渲染的复杂数据而导致的一系列问题

目录 1.页面效果及需求 2.遇到问题时的初始代码及问题 代码 问题 3.状态变化不能深层监听&#xff1f; 解答 4.使用了ObjectLink装饰器后为什么数据仍然无法被监听&#xff1f; Demo 结论 代码修改 5.子组件中定义一个箭头函数&#xff0c;在父组件中通过this.传入方…

leecode188.买卖股票的最佳时机IV

这道题目我在买卖股票III就已经得出规律了&#xff0c;具体可看买卖股票的最佳时机||| class Solution { public:int maxProfit(int k, vector<int>& prices) {int nprices.size();vector<vector<int>> dp(n,vector<int>(2*k1,0));for(int j1;j&l…

如何通过深度学习提升大分辨率图像预测准确率?

随着科技的不断进步&#xff0c;图像处理在各个领域的应用日益广泛&#xff0c;特别是在医疗影像、卫星遥感、自动驾驶、安防监控等领域中&#xff0c;大分辨率图像的使用已经成为了一项不可或缺的技术。然而&#xff0c;大分辨率图像带来了巨大的计算和存储压力&#xff0c;同…

【Spring Boot】SpringBoot自动装配-Import

目录 一、前言二、 定义三、使用说明 3.1 创建项目 3.1.1 导入依赖3.1.2 创建User类 3.2 测试导入Bean 3.2.1 修改启动类 3.3 测试导入配置类 3.3.1 创建UserConfig类3.3.2 修改启动类 3.4 测试导入ImportSelector 3.4.1 创建UseImportSelector类3.4.2 修改启动类3.4.3 启动测试…

操作系统课后题总复习

目录 一、第一章 1.1填空题 1.2单项选择题 1.3多项选择题 1.4判断题 1.5名词解释 1.6简答题 二、第二章 2.1填空题 2.2单项选择题 2.3 多项选择题 2.4判断题 2.5名词解释 2.6简答题 三、第三章 3.1填空题 3.2单项选择题 3.3多项选择题 3.4判断题 3.5名词解…