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: 调用处理器,用于处理代理类的方法调用
     */
    @CallerSensitive
    public 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 ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
    
    // 代理类名称前缀
    private static final String proxyClassNamePrefix = "$Proxy";

    // 原子计数器,用于生成唯一的代理类名
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public 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.proxy
        if (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());
        }
    }
    
    // 构造函数,调用父类构造函数存储InvocationHandler
    public $Proxy0(InvocationHandler h) {
        super(h);
    }
    
    // 实现接口方法
    @Override
    public 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方法。这样就实现了方法调用的拦截和处理。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ZYNQ初识6(zynq_7010)clock时钟IP核

基于板子的PL端无时钟晶振&#xff0c;需要从PS端借用clock1&#xff08;50M&#xff09;晶振 接下去是自定义clock的IP核封装&#xff0c;为后续的simulation可以正常仿真波形&#xff0c;需要注意顶层文件的设置&#xff0c;需要将自定义的IP核对应的.v文件设置为顶层文件&a…

深度学习模型格式转换:pytorch2onnx(包含自定义操作符)

将PyTorch模型转换为ONNX&#xff08;Open Neural Network Exchange&#xff09;格式是实现模型跨平台部署和优化推理性能的一种常见方法。PyTorch 提供了多种方式来完成这一转换&#xff0c;以下是几种主要的方法&#xff1a; 一、静态模型转换 使用 torch.onnx.export() t…

GPU 进阶笔记(一):高性能 GPU 服务器硬件拓扑与集群组网

记录一些平时接触到的 GPU 知识。由于是笔记而非教程&#xff0c;因此内容不求连贯&#xff0c;有基础的同学可作查漏补缺之用 1 术语与基础 1.1 PCIe 交换芯片1.2 NVLink 定义演进&#xff1a;1/2/3/4 代监控1.3 NVSwitch1.4 NVLink Switch1.5 HBM (High Bandwidth Memory) 由…

在Unity中用Ab包加载资源(简单好抄)

第一步创建一个Editor文件夹 第二步编写BuildAb&#xff08;这个脚本一点要放在Editor中因为这是一个编辑器脚本&#xff0c;放在其他地方可能会报错&#xff09; using System.IO; using UnityEditor; using UnityEngine;public class BuildAb : MonoBehaviour {// 在Unity编…

【贪心算法】贪心算法七

贪心算法七 1.整数替换2.俄罗斯套娃信封问题3.可被三整除的最大和4.距离相等的条形码5.重构字符串 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f…

(五)人工智能进阶:基础概念解释

前面我们介绍了人工智能是如何成为一个强大函数。接下来&#xff0c;搞清损失函数、优化方法和正则化等核心概念&#xff0c;才能真正驾驭它&#xff01; 1. 什么是网络模型&#xff1f; 网络模型就像是一个精密的流水线工厂&#xff0c;由多个车间&#xff08;层&#xff0…

SpringMVC(二)原理

目录 一、配置Maven&#xff08;为了提升速度&#xff09; 二、流程&&原理 SpringMVC中心控制器 完整流程&#xff1a; 一、配置Maven&#xff08;为了提升速度&#xff09; 在SpringMVC&#xff08;一&#xff09;配置-CSDN博客的配置中&#xff0c;导入Maven会非…

2、redis的持久化

redis的持久化 在redist当中&#xff0c;高可用的技术包括持久化&#xff0c;主从复制&#xff0c;哨兵模式&#xff0c;集群。 持久化是最简单的高可用的方法&#xff0c;作用就是备份数据。即将数据保存到硬盘&#xff0c;防止进程退出导致数据丢失。 redis持久化方式&…

【算法】模拟退火算法学习记录

写这篇博客的原因是博主本人在看某篇文章的时候&#xff0c;发现自己只是知道SGD这个东西&#xff0c;但是到底是个啥不清楚&#xff0c;所以百度了一下&#xff0c;然后在通过博客学习的时候看到了退火两个字&#xff0c;想到了本科做数模比赛的时候涉猎过&#xff0c;就上bil…

Visual Point Cloud Forecasting enables Scalable Autonomous Driving——点云论文阅读(12)

此内容是论文总结,重点看思路!! 文章概述 这篇文章介绍了一个名为 ViDAR 的视觉点云预测框架,它通过预测历史视觉输入生成未来点云,作为自动驾驶的预训练任务。ViDAR 集成了语义、三维几何和时间动态信息,有效提升了感知、预测和规划等自动驾驶核心任务的性能。实验表明…

AI 将在今年获得“永久记忆”,2028美国会耗尽能源储备

AI的“永久记忆”时代即将来临 谷歌前CEO施密特揭示了AI技术的前景&#xff0c;他相信即将在2025年迎来一场伟大的变化。AI将实现“永久记忆”&#xff0c;改变我们与科技的互动过程。施密特将现有的AI上下文窗口比作人类的短期记忆&#xff0c;难以持久保存信息。他的设想是…

工控主板ESM7000/6800E支持远程桌面控制

英创公司ESM7000 是面向工业领域的双核 Cortex-A7 高性能嵌入式主板&#xff0c;ESM6800E则为单核Cortex-A7 高性价比嵌入式主板&#xff0c;ESM7000、ESM6800E都是公司的成熟产品&#xff0c;已广泛应用于工业很多领域。ESM7000/6800E板卡中Linux系统配置为linux-4.9.11内核、…

越权漏洞简介及靶场演示

越权漏洞简介及靶场演示 文章目录 一、什么是越权&#xff1f; &#xff08;一&#xff09;越权漏洞的概念&#xff08;二&#xff09;越权漏洞的分类&#xff08;三&#xff09;常见越权方法&#xff08;四&#xff09;未授权访问 二、越权漏洞测试过程 &#xff08;一&…

VIT:视觉transformer|学习微调记录

一、了解VIT结构 vit提出了对于图片完全采用transformer结构而不是CNN的方法&#xff0c;通过将图片分为patch&#xff0c;再将patch展开输入编码器&#xff08;grid_size网格大小&#xff09;&#xff0c;最后用MLP将输出转化为对应类预测。 详细信息可以看下面这个分享&…

coredns报错plugin/forward: no nameservers found

coredns报错plugin/forward: no nameservers found并且pod无法启动 出现该报错原因 是coredns获取不到宿主机配置的dns地址 查看宿主机是否有dns地址 resolvectl status 我这里是配置正确后&#xff0c;如果没配置过以下是不会显示出dns地址的 给宿主机增加静态dns地址之后将…

使用Diffusion Models进行图像超分辩重建

Diffusion Models专栏文章汇总:入门与实战 前言:图像超分辨率重建是一个经典CV任务,其实LR(低分辨率)和 HR(高分辨率)图像仅在高频细节上存在差异。通过添加适当的噪声,LR 图像将变得与其 HR 对应图像无法区分。这篇博客介绍一种方式巧妙利用这个规律使用Diffusion Mod…

NineData 荣获年度“创新解决方案奖”

近日&#xff0c;国内知名 IT 垂直媒体 & 技术社区 IT168 再次启动“技术卓越奖”评选&#xff0c;由行业 CIO/CTO 大咖、技术专家及 IT 媒体多方联合评审&#xff0c;NineData 凭借技术性能和产品创新等方面表现出色&#xff0c;在数据库工具领域荣获“2024 年度创新解决方…

liunx下载gitlab

1.地址&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/ 安装 postfix 并启动 yum install postfix systemctl start postfix systemctl enable postfix ssh服务启动 systemctl enable sshd systemctl start sshd开放 ssh 以及 http 服务&#xff0c…

SQL—替换字符串—replace函数用法详解

SQL—替换字符串—replace函数用法详解 REPLACE() 函数——查找一个字符串中的指定子串&#xff0c;并将其替换为另一个子串。 REPLACE(str, old_substring, new_substring)str&#xff1a;要进行替换操作的原始字符串。old_substring&#xff1a;要被替换的子串。new_substri…

Android笔试面试题AI答之Android基础(11)

Android入门请看《Android应用开发项目式教程》&#xff0c;视频、源码、答疑&#xff0c;手把手教 文章目录 1.Android的权限有哪些&#xff1f;**1. 普通权限****常见普通权限** **2. 危险权限****权限分组****常见危险权限组及权限** **3. 特殊权限****常见特殊权限** **4. …