JDK动态代理的一个实验与分析

JDK动态代理为什么必须要基于接口 目录1. 前言2. 一个简单的例子2.1. 定义接口2.2. 接口实现类2.3. 自定义 `Handler`2.4. 测试2.5. 输出结果 1. 前言 JDK 动态代理的应用还是非常广泛的,例如在 Spring、MyBatis 以及 Feign 等很多框架中动态代理都被大量的使用,可以说学好 JDK 动态代理,对于我们阅读这些框架的底层源码还是很有帮助的 2. 一个简单的例子 在分析原因之前,我们先完整的看一下实现 JDK 动态代理需要几个步骤,首先需要定义一个接口 2.1. 定义接口 public in 阅读详情
package com.ssm.jdkproxy;

public interface Hello {
    public void sayHello();
    public String add(int a,int b);
}
package com.ssm.jdkproxy;

public class HelloImp1 implements Hello {


    public  HelloImp1(){

    }
    public  HelloImp1(int i){
        System.out.println(i);
    }
    @Override
    public void sayHello() {
        System.out.println("执行了sayHello方法");
    }
    public String add(int a,int b){
        int c=a+b;
        System.out.println("执行了add方法"+c);
        return ("执行了add方法"+c);
    }
    public void hey(){
        System.out.println("hey");
    }
}
package com.ssm.jdkproxy;

import java.lang.reflect.*;
import java.util.logging.Filter;

public class JdkProxyExample implements InvocationHandler {
    private Object target = null;


    public Object bind(Object target){
        this.target= target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理对象所属的类"+proxy.getClass());
        System.out.println("传进来的方法"+method.toString());
        System.out.println("参数列表"+(args!=null?args:"没有参数"));
        System.out.println("调度真实对象之前");
        Object obj = method.invoke(target,args);
        System.out.println("调度真实对象结束");
        System.out.println();
        return obj;
    }

    public static void main(String[] args) {
        JdkProxyExample jdk = new JdkProxyExample();
        Hello proxy = (Hello)jdk.bind(new HelloImp1());
        System.out.println(proxy.getClass());
        Class c = proxy.getClass();

        Class[] inter = c.getInterfaces();
        System.out.println("------------------"+"实现的接口-----------------------");
        for (Class aIn:inter
        ) {
            System.out.println(aIn);
        }
        System.out.println();

        System.out.println("------------------"+c+"的所有构造器-----------------------");
        Constructor[] con = c.getConstructors();
        for (Constructor aCon:con
             ) {
            System.out.println(aCon);
        }
        System.out.println();

        System.out.println("------------------"+c+"的所有字段----------------------");
        Field[] fields = c.getDeclaredFields();
        for (Field f: fields
        ) {
            System.out.println(f);
        }
        System.out.println();

        System.out.println("------------------"+c+"的所有方法----------------------");
        Method[] methods = c.getDeclaredMethods();
        for (Method method:methods
        ) {
            System.out.println(method);

        }
        System.out.println();


        System.out.println("-------------"+"反射创建代理类对象并调用方法"+"-----------");
        try {
            Object aProxy = con[0].newInstance(jdk);
            System.out.println(aProxy.equals(proxy));
            Method aMeth = aProxy.getClass().getDeclaredMethod("add",int.class,int.class);
            aMeth.invoke(aProxy,1,2);
        } catch (Exception e) {
            e.printStackTrace();
        }


        proxy.add(1,2);
    }
}

程序的执行结果:

class com.sun.proxy.$Proxy0
实现的接口
interface com.ssm.jdkproxy.Hello

class com.sun.proxy.$Proxy0的所有构造器:
public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

class com.sun.proxy.$Proxy0的所有函数:
public final java.lang.String com.sun.proxy.$Proxy0.add(int,int)
public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)
public final java.lang.String com.sun.proxy.$Proxy0.toString()
public final int com.sun.proxy.$Proxy0.hashCode()
public final void com.sun.proxy.$Proxy0.sayHello()

反射执行add方法
代理对象所属的类class com.sun.proxy.$Proxy0
传进来的方法public abstract java.lang.String com.ssm.jdkproxy.Hello.add(int,int)
参数列表[Ljava.lang.Object;@4b67cf4d
调度真实对象之前
3
调度真实对象结束

代理对象所属的类class com.sun.proxy.$Proxy0
传进来的方法public abstract java.lang.String com.ssm.jdkproxy.Hello.add(int,int)
参数列表[Ljava.lang.Object;@7ea987ac
调度真实对象之前
3
调度真实对象结束

    首先可以看出一点:JDK动态代理虽然是对对象的代理,但是同时生成了代理类,代理类的Class文件存放在com.sun.proxy包之下.也就是说

Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);

这个语句首先生成了一个Class文件,新建了一个类,然后返回了一个类的实例.    

    我们再来看一下这个类都定义了那些东西,通过反射查看这个com.sun.proxy.$Proxy0类

实现的接口

interface com.ssm.jdkproxy.Hello

通过反射我们查看到这个类只实现了一个接口也就是与被代理的实际对象所实现的接口,这也是为什么JDK动态代理一个对象的时候,其所属的类必须实现接口.

构造器

可以看到,不管真实类有多少构造器,代理类只有一个构造器:

public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

这个构造器只有一个参数,就是一个实现了InvocationHandler接口的对象.

结合一下前面的内容我们现在大概知道newProxyInstance方法做了什么事情了.

Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);

这个方法首先通过传入的类加载器和相应的接口信息生成了一个类,然后通过第三个参数作为构造器的参数传递给了com.sun.proxy.$Proxy0的构造器,生成了一个com.sun.proxy.$Proxy0类的对象.

上面的过程是newProxyInstance函数自动执行的. 我们现在已经得到com.sun.proxy.$Proxy0的构造器了,我们手动来调用一下这个构造器.

        try {
            Object aProxy = con[0].newInstance(jdk);
            System.out.println(aProxy.equals(proxy));
            Method aMeth = aProxy.getClass().getDeclaredMethod("add",int.class,int.class);
            aMeth.invoke(aProxy,1,2);
        } catch (Exception e) {
            e.printStackTrace();
        }

输出结果:

false
代理对象所属的类class com.sun.proxy.$Proxy0
传进来的方法public abstract java.lang.String com.ssm.jdkproxy.Hello.add(int,int)
参数列表[Ljava.lang.Object;@7ea987ac
调度真实对象之前
3
调度真实对象结束

这个结果和直接使用newProxyInstance方法生成的对象具有同样的效果,这也验证了我们之前的猜想.newProxyInstance先创建了一个类,然后使用当前对象作为构造器参数构造了一个代理对象.

有一点需要注意一下,看一下getDeclaredMethods()的结果显示有equals()方法,说明代理类重写了equals方法. 但是我们这一句System.out.println(aProxy.equals(proxy))的执行结果是false,也就是谁虽然看起来我们是使用相同的JdkProxyExample对象来作为构造器参数构造代理对象,但是产生的对象还是不同的.因为没有去找com.sun.proxy.$Proxy0的class文件进行反编译,所以也不知道具体原因是什么.

拥有的字段

private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m1
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m4
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m2
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m3
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m0

可以看出代理类拥有五个实例变量,而且都是静态Method变量,刚好对应代理类中的五个方法.但实际存放的是实际对象的Method. 在使用代理调用方法是作为真实的函数传递给invoke函数.

实现的方法

class com.sun.proxy.$Proxy0的所有函数:
public final java.lang.String com.sun.proxy.$Proxy0.add(int,int)
public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)
public final java.lang.String com.sun.proxy.$Proxy0.toString()
public final int com.sun.proxy.$Proxy0.hashCode()
public final void com.sun.proxy.$Proxy0.sayHello()

主要看两个方法,也就是我们在接口中定义的方法add和sayHello.这两个方法都被代理类所实现了.然后再看一下HelloImp1中定义的方法hey()在代理类中并没有被实现. 所以我们可以得到结论,JDK动态代理只会实现接口中所定义的方法,而不是实现代理类的所有方法.

直接使用proxy执行方法

代理对象所属的类class com.sun.proxy.$Proxy0
传进来的方法public abstract java.lang.String com.ssm.jdkproxy.Hello.add(int,int)
参数列表[Ljava.lang.Object;@12a3a380
调度真实对象之前
执行了add方法3
调度真实对象结束

可以看到,调用代理类的方法执行了invoke函数.那我们应该很容易想到,代理类方法的实现其实只是在调用invoke函数(就是我们作为构造器参数的那个对象所属类中的invoke函数).

invoke函数

我们继续看一下invoke函数.

public Object invoke(Object proxy, Method method, Object[] args)

的参数,一个proxy,一个method对象,一个参数列表.

这里就很好理解了,我们在为代理类传了一个JdkProxyExample的对象作为参数,在代理类的方法实现中使用这个对象来调用其invoke方法,传的三个参数分别是,当前proxy对象,对proxy对象调用的method,调用方法传入的参数列表. 我们可以通过代码来验证一下这个猜想,具体代码就不放上来了.

 

总结

所以我们来总结一下,JDK动态代理.

首先我们要使用JDK动态代理首先要有一个实现接口的类,然后一个实现InvocationHandler接口的代理逻辑实现类.代理逻辑的实现类当中要有一个public Object invoke(Object proxy, Method method, Object[] args)方法.

我们可以通过

Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),InvocationHandler handler)

来创建一个代理类的实例,该方法有三个参数: (真实类的类加载器,真实类实现的接口的Class对象列表,实现了InvocationHandler接口的类的实例对象),前面两个参数的作用是,使用这两个信息来构造一个代理类的Class文件并进行加载(也就是说动态代理实际上创建了新的加载到方法区的类),最后一个参数的作用是 对代理类的函数调用将通过发送给这个对象的invoke函数来实现.

 

最后放一个$Proxy0的源码(不是本例的):

//$proxy0的源码
public final class $Proxy0 extends Proxy implements Subject {  
    private static Method m1;  
    private static Method m0;  
    private static Method m3;  
    private static Method m2;  
  
    static {  
        try {  
            m1 = Class.forName("java.lang.Object").getMethod("equals",  
                    new Class[] { Class.forName("java.lang.Object") });  
  
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                    new Class[0]);  
  
            m3 = Class.forName("接口的实现类的路径").getMethod("实现类的方法",  
                    new Class[0]);  
  
            m2 = Class.forName("java.lang.Object").getMethod("toString",  
                    new Class[0]);  
  
        } catch (NoSuchMethodException nosuchmethodexception) {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        } catch (ClassNotFoundException classnotfoundexception) {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    } //static  
  
    public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  
    }  
  
    @Override  
    public final boolean equals(Object obj) {  
        try {  
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final int hashCode() {  
        try {  
            return ((Integer) super.h.invoke(this, m0, null)).intValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    public final void 接口的实现类的方法() {  
        try {  
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final String toString() {  
        try {  
            return (String) super.h.invoke(this, m2, null);  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
}

 

 

jdk动态代理实现原理 JDK 动态代理是什么 在了解JDK动态代理前,有需要可以了解下代理模式 参考:https://blog.youkuaiyun.com/yhl_jxy/article/details/52679882 JDK 的动态代理是基于拦截器和反射来实现的 JDK代理是不需要第三方库支持的,只需要 JDK 环境就可以进行代理,使用条件: 必须实现 InvocationHandler 接口 使用 Proxy.newProxyInstance 产生代理对象 被代理的对象必须要实现接口 JDK 动态代理简单实现 使用 JDK 动态 阅读详情

相关推荐

jdk 可以多层嵌套动态代理

以前一直以为jdk的动态代理不可以多层嵌套,今天做了一个实验发现是可以的。记录一下 类如下 public interface DemoService { void saiHi(String name); } public class DemoServiceImpl implements DemoService { @Override public void saiHi(...

zhenghuangyu的博客 1408

【SSM】15-动态代理模式的使用总结

目录 一、代理模式 二、静态代理 (一)静态代理 (二)静态代理简单实现 三、动态代理 (一)动态代理 (二)动态代理简单实现 四、动态代理原理分析 五、InvocationHandler接口和Proxy类详解 六、JDK动态代理和CGLIB动态代理代码示例比较与总结 (一)定义创建用户管理接口 (二)用户管理实现类,实现用户管理接口(被代理的实现类) (三)采用JDK代理实现:JDK动态...

m0_50964976的博客 545

深入JDK动态代理:源码分析与实践应用

JDK动态代理是Java技术中实现面向切面编程(AOP)的关键技术之一。与传统的静态代理相比,JDK动态代理提供了一种更加灵活的方式来动态创建代理对象。开发者无需为每个类编写代理代码,而是可以在运行时通过指定的接口生成对应的代理实例。动态代理的核心在于其代理对象的创建是基于接口而非具体的实现类。这种基于接口的代理模式,使得同一个代理类可以代理多个接口,具有很强的复用性。同时,动态代理支持在代理对象的方法调用前后插入特定的逻辑,使得代码的维护和扩展变得更为简便。

weixin_42596011的博客 1026

SSM框架day01——021——JDK动态代理模式、022-JDK动态代理模式的实现

JDK的Proxy动态代理        1) 使用要求:目标类必须要实现业务接口。即,使用JDK的Proxy动态代理,要求目标类必须是实现接口的类。    2)代理对象的名称:        名称由三部分构成:$ + Proxy + 数字。数字表示当前的JDK的Proxy所生成的代理类的索引。索引从0开始计数JDK动态代理模式的实现1.用户类2.委托类...

sanmu_1的博客 321

PowerDesigner16.5下载和安装教程

一、软件下载 1.下载地址(不用复制,直接点击下载链接即可):链接:https://pan.baidu.com/s/1MSnWHWt3SO2Uz5dt9AANAg提取码:z9kk 二、软件安装 1.下载安装包(包含安装文件、汉化包、注册文件) 2.下载后文件内容如下 3....

cicong2374的博客 10万+

ISP调试工程师相关面试问题_001

Qualcomm、MTK等ISP pipeline的框架原理及算法原理,还有调试经验。

notgoodenouf的博客 3547

关于JDK动态代理的自我理解

前言:        关于学习,我总是在探索适合自己的理解方式,因为我是理科思维,思考方式偏向于"因为-所以",因此思考事情喜欢弄清前因后果、来龙去脉、前世今生...我一直相信坚信:知其然更要知其所以然,无论技术还是人生。        从学生时代copy老师的解题步骤,到懂得照猫画虎、举一反三,一路到如今学会独立思考、深究底层、换位为开发者进行考虑,我认为最适合自己的学习方法是:     ...

SaMo_Hello的博客 258

从寄存器到驱动:深入解读RFSoC RF-ADC阈值与校准API的底层逻辑

本文深入解析Xilinx RFSoC中RF-ADC的阈值检测与校准API的底层逻辑,涵盖寄存器映射、驱动实现及Gen3增强功能。通过详细代码示例和寄存器操作流程,揭示如何精准控制信号阈值检测与校准时序,提升系统稳定性与性能。特别探讨RF数据转换器在无线通信和雷达信号处理中的关键应用。

weixin_30552811的博客 687

动态代理的一些简单的总结

快速入门动态代理,学完动态代理的一些自我简单的总结

sunao1106的博客 287

【Godot】实现对话系统

在 Godot 中实现对话系统(Dialogue System)是游戏开发中的常见需求。Godot本身自带的的灵活性和轻量级脚本语言 GDScript 使得实现对话系统变得相对简单。以下是实现一个基础对话系统的示例。(语言为GDScript但高亮选择Python因为类似)通过 Godot 和 GDScript,我们可以给我们的游戏实现一个对话系统。从简单的文本显示到复杂的分支对话,Godot 提供了足够的灵活性来满足我们的需求。如果需要更高级的功能,可以尝试使用现成的插件如 Dialogic。

Octopus2077的博客 2519

树莓派34/100 - Pico读取MPU6050加速度和陀螺仪传感器数据

树莓派Pico连接MPU-6050模块(三轴陀螺仪+三轴加速度),读取运动姿态数据。

申龙斌的程序人生 5087

Arrays类讲解

Arrays讲解 package com.li.array; import java.util.Arrays; public class ArrayDemo06 { public static void main(String[] args) { int[] a = {1,2,52,66,22,7,8,777,8843,3243}; System.out.println(a); //输出的结果是[I@12a3a380 //打印数组元素 Ar

weixin_60194919的博客 247

手把手教你用CV5200模块搭建无人机高清图传系统(含实测数据)

本文提供了一份基于CV5200模块搭建远距离无人机高清图传系统的完整实战指南。文章详细解析了系统架构、硬件连接、软件配置与优化,并分享了实地飞行测试数据。核心在于利用CV5200的LR-WiFi私有协议,在视距下实现数公里级的稳定高清视频传输,为DIY爱好者和行业应用提供了高性价比的解决方案。

day7的博客 719

JDK动态代理和CGLIB动态代理案例分析

JDK动态代理和CGLIB动态代理案例分析

我从不打没有准备的仗! 9765

Silvaco TCAD仿真4——DeckBuild实战:从示例库到自定义设计

本文详细介绍了Silvaco TCAD仿真工具DeckBuild的实战应用,从示例库的使用到自定义设计的全过程。通过具体案例和参数修改技巧,帮助用户快速掌握TCAD仿真技术,提升半导体器件设计的效率和精度。特别适合需要从基础入门到进阶操作的工程师和研究人员。

weixin_29284657的博客 434

[AI] 本地部署 Dify 遇到 Internal Error?一文彻底解决密码不一致引发的问题

摘要:Dify本地部署常见InternalError错误主要由配置残留或密码不一致导致,特别是修改.env文件后未同步清除数据库记录。彻底解决方案包括:1)清理Docker数据;2)删除旧配置和数据库;3)还原源代码;4)重建.env文件;5)重新初始化部署。关键提醒:记录部署路径、备份.env文件并妥善保存密码。该错误本质是配置冲突,最有效方法是完全重置而非调试,保持清晰的部署管理可避免多数InternalError问题。(149字)

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 434

JDK动态代理源码解析

本篇从源码的角度,对JDK动态代理的实现,工作原理做简要分析。

西岭千秋雪的博客 1692

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Java——JDK动态代理

动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的。动态代理不需要定义代理类的,java源文件。动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。动态代理的实现方式常用的有两种:使用JDK代理,与通过CGLlB动态代理。

m0_73671382的博客 7677

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

JDK动态代理与CGLIB动态代理

设计模式之代理模式 下面假设一个常见的场景,假如你有一个法国的朋友叫Aice,有一天你想要购买一瓶法国香奈儿的香水,这种香水只在法国销售。你找到你的朋友Alice,请求她帮忙购买一瓶香奈儿的香水,你的朋友Alice于是乎就找到香奈儿的销售店购买了一瓶法国香奈儿的香水,并将香水交给了你。 在上面这个例子当中,你想要购买法国香水,但是无法直接向香奈儿供应商购买,只能通过中间人(Alice)购买,这就是我们生活中比较常见的代理模式。我们假设有一天,你突然想要一瓶法国红酒,但是由于Alice本身并不具备购买红

weixin_43529092的博客 3323

JDK动态代理源码分析

JDK动态代理简单案例源码分析大概流程jdk动态代理涉及到的主要类Proxy.javaWeakCache.javaProxyClassFactory.javaProxyGenerator.java生成代理类$Proxy0.class代理类$Proxy0.class执行流程 JDK动态代理是代理模式的一种实现方式,其只能代理接口。 简单案例 1.首先新建一个接口Subject public inte...

yu_kang的博客 3413

【spring】JDK动态代理的使用与源码分析

JDK动态代理的实现是在运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具类去生成一个代理类和代理类实例。 JDK动态代理的使用 编写一个类实现InvocationHandler接口,主要负责需要增强的方法的增强逻辑。 package com.morris.spring.demo.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public cl

morris 1万+

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

内存优化:单接口直接使用接口Class对象作为缓存key,而多接口需要克隆数组并转换为List,会产生额外的对象创建 缓存key的复杂度:单接口的key就是一个Class对象,更简单高效,而多接口的key是L ist

xweiran的博客 1182

JDK动态代理的执行过程分析

目录 JDK动态代理的执行过程分析 JDK动态代理和CGLib动态代理的区别 JDK动态代理的执行过程分析 前提:创建一个目标类和测试类进行动态代理执行过程的分析 目标类:CalculatorService package com.jd.calculator; public class CalculatorService implements ICalculatorServ...

qq_42865575的博客 1301

jdk动态代理和cglib动态代理的区别及分析

代理的作用:提供代理加强对一个对象的访问 jdk动态代理的实现原理: 拿到被代理类的引用,并获取所有接口信息(反射获取) jdk proxy.newInstance生成新的类,实现接口方法 动态生成java代码,增强逻辑代码也写入 编译java代码生成class文件 加载并运行新的class jdk动态代理调用过程: 生成代理对象的编译文件$proxy0.class 加载到内存当中...

林伟杰的博客 1935

动态代理及JDK动态代理源码分析

JAVA的动态代理机制可以动态的创建代理并动态的处理代理方法调用,只要简单指定一组接口及为拖累对象,就能动态的获取代理类

hacke2' Blog 9130
上一篇: HashMap源码解析(三) HashMap的put与get操作
codexjli
博客等级 码龄11年 2粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值