JDK动态代理使用及原理解析

windows server 2012 AD 活动目录部署系列(十一)安装证书服务器(ADCS) 本篇博文介绍如何在AD域控上安装证书服务器,本实验域环境:yun.com。 步骤如下: 1、打开“服务器管理器”,点击“添加角色和功能”,如下图: 2、点击下一步, 3、点击下一步, 4、点击下一步, 5、点击“Active Directory证书服务”,弹出功能选择,点击"添加功能",如下图, 阅读详情

JDK动态代理使用及原理解析

一、动态代理的使用

1.1 动态代理简单示例

    
动态代理类图如下:

avatar

    
主体和实际主体代码如下所示:

/**
 * 主体
 */
public interface Subject {
    //主体方法
    void subjectMethod();
}

/**
 * 实际主体
 */
public class RealSubject implements Subject {
    @Override
    public void subjectMethod() {
        System.out.println("执行方法subjectMethod...");
    }
}

    
代理类代码如下所示,可看到代理类中使用了反射调用:

public class ProxyHandler implements InvocationHandler {
    private Subject subject;

    public ProxyHandler(Subject subject) {
        this.subject = subject;
    }

    /**
     * @param proxy  调用方法的代理实例
     * @param method 被代理对象中的方法
     * @param args   调用方法时的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行方法subjectMethod前");
        /**
         * method.invoke(Object obj, Object... args)
         * @param obj  :方法被调用的对象
         * @param args :方法被调用的参数
         * @return Object: 方法调用返回的结果
         */
        Object o = method.invoke(subject, args);
        System.out.println("执行方法subjectMethod后");
        return o;
    }
}

    
请求者中声明了实际主体和调动处理程序:

/**
 * Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
 * @param loader:代理类的类加载器
 * @param interfaces: 代理类要实现的接口列表
 * @param h:将方法调用分派到的调用处理程序
 * @return 返回代理对象,是对被代理对象的增强
 */
Subject proxySubject = (Subject) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(),
        new Class<?>[]{Subject.class}, new ProxyHandler(new RealSubject()));
proxySubject.subjectMethod();

    
输出如下:

执行方法subjectMethod前
执行方法subjectMethod...
执行方法subjectMethod后

1.2 创建代理实例在代理类中

    
在实际应用中,创建代理实例(Proxy.newProxyInstance)往往是放在代理类中
avatar

    
代码如下所示:

public class ProxyHandlerOther implements InvocationHandler {
    private Subject subject;

    public Subject getProxySubject(Subject subject) {
        this.subject = subject;
        return (Subject) Proxy.newProxyInstance(this.subject.getClass().getClassLoader(),
                this.subject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行方法subjectMethod前");
        Object o = method.invoke(subject, args);
        System.out.println("执行方法subjectMethod后");
        return o;
    }
}

    
请求者直接创建代理并调用方法即可:

Subject otherProxySubject = new ProxyHandlerOther().getProxySubject(new RealSubject());
otherProxySubject.subjectMethod();

1.3 公用的代理类

    
可将代理类中的代理对象和通知公用出来,如下所示:
avatar

    
代码如下:

public class ProxyHandlerCommon implements InvocationHandler {
    private Object subject;
    private Advice advice;

    public Object getProxyObject(Object subject, Advice advice) {
        this.subject = subject;
        this.advice = advice;
        return Proxy.newProxyInstance(this.subject.getClass().getClassLoader(),
                this.subject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        advice.before();
        Object o = method.invoke(subject, args);
        advice.after();
        return o;
    }
}

    
通知接口和默认通知类如下:

/**
 * 公用通知接口
 */
public interface Advice {
    void before();
    void after();
}

/**
 * 默认通知
 */
public class AdviceDefault implements Advice{

    @Override
    public void before() {
        System.out.println("Advice before...");
    }

    @Override
    public void after() {
        System.out.println("Advice after...");
    }
}

    
请求者直接指定主体和通知即可:

//公用的代理类
Object commonProxy = new ProxyHandlerCommon().getProxyObject(new RealSubject(), new AdviceDefault());
((Subject) commonProxy).subjectMethod();

如何进入到代理类中的invoke的?

    
请求者中使用Proxy.newProxyInstance生成了代理实例,那么代理实例是如何进入到代理类中的invoke方法的呢?

    
我们在请求者中设置断点如下图所示:
avatar
    
如上图可看到,代理实例实际上是一个名为$Proxy0的一个类,内部有个h实例(ProxyHandler),h实例内部有一个subject(RealSubject)。

    
猜测:这个$Proxy0也实现了Subject接口,执行主体方法subjectMethod时实际上是去执行h实例里的invoke方法。

拿到$Proxy0这个类,有以下两种方式:

2.1 拿到$Proxy0这个类

方法一:saveGeneratedFiles属性

    
找源码可知打开saveGeneratedFiles属性即可,在请求者中加上该属性:

//保存生成的代理类class文件,默认false
 System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

    
运行程序时,会看到项目下生成 P r o x y 0. c l a s s 这 个 类 ( 一 般 位 于 c o m . s u n . p r o x y . Proxy0.class这个类(一般位于com.sun.proxy. Proxy0.class这个类(一般位于com.sun.proxy.Proxy.class)。

    
为什么加上该属性就可打印文件,后面会说到。
    

方法二、从内存中把 P r o x y 0 写 入 到 文 件 Proxy0写入到文件 Proxy0写入到文件Proxy0.class

public static void createProxyClassFile(){
    /**
     * JDK自带的工具,把内存里的字节码转换字节码数组
     * 从内存中把$Proxy0写入到文件$Proxy0.class中
     * 参数1:类名
     * 参数2:该类实现的接口
     */
    byte [] $Proxy0Byte = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Subject.class});
    try{
        //字节码数组写入到文件
        FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class");
        fileOutputStream.write($Proxy0Byte);
        fileOutputStream.close();
    }catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.2 $Proxy0的解析

    
$Proxy0.class源码如下:

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m3;
    //...省略
    public final void subjectMethod() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    static {
        //...省略
        m3 = Class.forName("com.mm.mmblogs.b4.Subject").getMethod("subjectMethod");
        //...省略
    }
}

由上可知:

  • $Proxy0实现了Subject接口,所以它也有subjectMethod方法
  • P r o x y 0 继 承 了 Proxy0继承了 Proxy0继承了Proxy类,里面的变量InvocationHandler即h就是Proxy.newProxyInstance时传入的ProxyHandler实例
  • $Proxy0中subjectMethod方法调用了h中的invoke方法

Proxy.newProxyInstance是如何生成一个代理对象然后返回的?

  • 下面代码经过手动删减

    
进入到Proxy.newProxyInstance方法,代码大致如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        final Class<?>[] intfs = interfaces.clone();

        //生成代理类对象
        Class<?> cl = getProxyClass0(loader, intfs);

        //拿到代理类的构造函数
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        
        //返回代理实例
        return cons.newInstance(new Object[]{h});
    }

    
再来看看getProxyClass0方法:

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
    // 接口数量不可大于065535
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // 缓存中存在,则直接返回
    // 否则通过ProxyClassFactory创建代理类
    return proxyClassCache.get(loader, interfaces);
}

    
ProxyClassFactory类如下:

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) {
        
            //生成指定的代理类的字节码文件
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
    
            //解析字节码,生成$Proxy的Class对象并返回
            return defineClass0(loader, proxyName,
            		proxyClassFile, 0, proxyClassFile.length);

            }
        }
    }

    
继续跟进ProxyGenerator.generateProxyClass方法,这里还看到了sun.misc.ProxyGenerator.saveGeneratedFiles属性:

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    //生成指定的代理类的字节码文件
    final byte[] var4 = var3.generateClassFile();
    //判断sun.misc.ProxyGenerator.saveGeneratedFiles属性,如果为true则保存生成的代理类class文件
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                //...省略
                Files.write(var2, var4, new OpenOption[0]);
                return null;
            }
        });
    }

    return var4;
}

    
generateClassFile方法中,主要是为代理类组装信息,如组装接口中的所有方法、组装构造函数、组装字段信息等等,然后写入文件,最后以字节数组的方式返回。

    
Proxy.newProxyInstance方法总结如下:

  • 生成代理类对象,如果缓存没有则创建。创建时先生成指定的代理类的字节码文件,然后在解析称class对象返回。
  • 拿到代理类的构造函数,并创建代理实例返回。
  • sun.misc.ProxyGenerator.saveGeneratedFiles属性,如果为true则保存生成的代理类class文件,默认为false。
  • 代理类工厂ProxyClassFactory生成的代理类名前缀为$Proxy,且用AtomicLong保证各个代理类名唯一。
【DRAM存储器六十六】DDR5介绍--训练相关之CA/CS Training 详述DDR5 CA/CS training过程细节 阅读详情

相关推荐

十一、机器学习进阶知识:径向基神经网络(RBFNN)的实现(Python,附源码及数据集)

本文对径向基神经网络(RBFNN)的理论基础及建模步骤进行介绍,之后使用Python实现基于RBFNN的数据预测,通俗易懂,适合新手学习,附源码及实验数据集。

weixin_42051846的博客 1万+

使用动态代理,在Debug模式下,代理对象显示为null的分析

1、准备一个接口: 2、准备接口的实现类,类中重写接口的方法: 3、准备一个代理对象工厂,提供一个IService类型的成员变量,一个获取IService类型的代理对象的方法: 4、编写测试类,获取代理对象,调用被代理对象的find()方法 5、在代理工厂类中的获取代理对象的方法中,图片中红框的位置上打断点。 6、Debug运行,查看得到的代理对象proxyServi...

【欢迎关注公众号:冬瓜白】 1466

智能车图像处理去畸变+逆透视教程

图像处理,去畸变与逆透视整合

wu58430的博客 1万+

将JDK动态代理生成的类保存为 .class文件System.setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles“, “true“)无效

将JDK动态代理生成的类保存为 .class文件System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");无效,没有看到相应的字节码文件,这是因为System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");为jdk8版本及之前的设置。 文章目录1.jdk8及之前的设置:2.jdk8之后的设置:3.如何知道是jdk.proxy.Proxy

MrYushiwen的博客 4354

windows10 + wsl2安装docker

一、安装wsl https://docs.microsoft.com/zh-cn/windows/wsl/install-win10 1.1以管理员身份运行powershell 1.2执行如下命令 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 1.3重启电脑 1.4 重启电脑后,如果需要安装wsl2, 则安装官方文档继续后续操作,因我...

happytree001的博客 1561

Qt QChart实战:从零打造一个实时温度监控仪表盘(附完整源码)

本文详细介绍了如何使用Qt QChart模块从零构建一个工业级实时温度监控仪表盘。通过实战案例,展示了波形图的绘制、坐标轴配置、数据流模拟等关键技术,并提供了完整的源码实现,帮助开发者快速掌握Qt在数据可视化领域的应用。

weixin_42537919的博客 235

java原生动态代理JDK Proxy

JDK Proxy是我们最容易使用的代码自动生成方案

老螺丝的技术人生 1229

从光耦隔离到稳定驱动:信捷伺服与STM32的硬件设计陷阱与救赎

本文深入探讨了信捷伺服驱动器与STM32微控制器配合时的硬件设计关键问题,包括光耦隔离电路选型、电源电压选择、拨码开关设置等核心环节。针对伺服驱动器的稳定驱动需求,提供了从电路设计到抗干扰策略的完整解决方案,帮助工程师规避常见陷阱,提升工业控制系统的可靠性。

gold8的专栏 306

jvm原理(36)透过字节码生成审视Java动态代理运作机制

我们在使用spring这类框架的时候,基于动态代理的使用,比如AOP,会使得开发更加灵活,那么在字节码的层面动态代理是什么样子的呢,生成出来的代理类结构是什么,本次我们首先写一个动态代理的例子,然后得到生成的动态代理类。 定义接口: public interface SubJect { void request(); } 定义实现类: public class RealSubJect i...

wzq6578702的专栏 794

[24] cuda 应用之 nppi 实现图像颜色通道变换

[24] cuda 应用之 nppi 实现图像颜色通道变换

yohnyang的博客 340

Python的单元测试之unittest

是一个功能类似 unittest 的库,它集合众家标准测试库之所长,和 unittest 相比,功能更为强大,使用更为简单,OpenStack 广泛的利用它编写单元测试。unittest 提供了自动匹配发现并执行测试用例的功能,随着项目代码结构越发庞大,势必有多个测试文件,自动匹配发现并测试用例的功能在此就显得非常有用,只要满足。unittest 提供了丰富的命令行入口,可以根据需要执行某些特定的用例。的测试用例都会被 unittest 发现并执行,测试用例文件的默认匹配规则为 test*.py。

1、测试博主技术知识分享 2、pos行业知识技术分享 3、java后端技术知识分享 4、嵌入式技术知识分享 5、运维部署相关知识分享 271

Java代理初探

在日常的开发中,需求的变动是很正常的,而且也是经常发生的,比如在我们的开发中有如下一个接口 public interface UserService { User query(User user); void save(User user); } 有如下的实现类,最开始的实现是这样的。 public class UserServiceImpl implements UserService { @Override public User query(User user

码农笔记 285

dify进阶:使用 FastAPI 的 StreamingResponse 实现高效的数据流传输

在 FastAPI 框架中,是一个非常有用的工具,它允许你以流的方式发送数据给客户端,而不是一次性加载所有内容到内存中再发送。这对于处理大型文件下载、视频流或者任何需要长时间处理的任务特别有用。

xiezhipu的博客 1431

动态代理——JDK动态代理原理&示例解析(图文并茂)

觉得可以的话点个关注,转个发,加个收藏呗,陆续奉上干货~~~~ 开始~~ 动态代理在Java中有JDK动态代理和CGLIB动态代理,也就是在运行中动态生成字节码并加载到虚拟机中供我们后续使用。下面我们先看下这两种方式的区别。 两者区别: JDK动态代理:需要被代理对象的类实现了某些接口,生成的代理类也会实现相应的接口 CGLIB动态代理:不需要被代理对象的类实现了某些接口,生成的代理类为目标对象的类的子类 下面我们对JDK动态代理进行解析:(源码见下方) 1、java.lang.refle

qq_22772259的博客 9581

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

JDK动态代理原理解析

1、回顾一下JDK动态代理的核心参数 如果我们要为target类创建一个【JDK动态代理对象】,那么我们必须要传入如下三个核心参数 加载target类的类加载器 target类实现的接口 InvocationHandler 为什么必须要这三个参数呢?之前使用动态代理的时候都是直接按接口要求传这三个参数,但从来没想过为什么?下面仔细去探究一下 2、实现一个简单的动态代理 【JDK动态代理】的核心其实是借助【Proxy.newProxyInstance(ClassLoader loader,Class&lt

wenpan的博客 4958

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

深入探索JDK动态代理:从入门到原理的全面解析

为了调试目的,我们要查看或修改这些动态生成的代理类,正常我们还需要反编译工具(比如JD-GUI或JAD)来查看生成的类的源代码,但是我们不用那么麻烦,直接用IDEA打开即可。在标准的Java JDK实现中,生成的动态代理类(字节码)默认是在内存中动态生成并直接加载的,不会写入磁盘成为文件。现在,我们可以编写一个测试类来验证动态代理的使用,这个例子中,我们通过动态代理实现了一个代理对象,当调用代理对象的方法时,会先执行。添加了这个系统属性后,当创建动态代理实例的时候,JVM会将生成的代理类以。

Apple_Boy的博客 3249

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

JDK动态代理的实现和原理解析(基于JDK1.7)

一、什么是代理代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。详细介绍请参考:java设计模式之代理模式。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委

想作会飞的鱼的博客 8731

Jdk动态代理原理解析

Jdk动态代理原理解析

qq_18846731的博客 100

源码解析JDK动态代理原理

通过解析JDK动态原理的Class文件,借助源码,理解JDK动态代理的实现过程。

记录 575

解析JDK动态代理实现原理

JDK动态代理使用实例 代理模式的类图如上。关于静态代理的示例网上有很多,在这里就不讲了。 因为本篇讲述要点是JDK动态代理的实现原理,直接从JDK动态代理实例开始。 首先是Subject接口类。 package proxy.pattern; ​ public interface Subject { void request() throws Exception; } 接着是RealSubject类。 package proxy.pattern; ​ public class RealSubje

274

JDK8动态代理实现与原理解析

第一部分:JDK8动态代理基本实现 1,先定义一个接口 public interface UserService { void work(String workContent); } 2,再定义一个接口实现类 public class StudentService implements UserService { @Override public void work(St...

iamcodingmylife的专栏 998

JDK动态代理简单使用方法+通过Proxy和InvocationHandler源码解析原理

一、JDK动态代理的使用 JDK动态代理模式是基于接口,所以我们先定义一个Moveable接口: public interface Moveable { void go(); } 再定义一个需要被代理的Car类: public class Car implements Moveable { @Override public void go() { Syst...

进修的CODER 861

Java——深入解析 CGlib 与 JDK 动态代理:原理、区别与实践

/ 目标接口void add();// 目标类(实现接口)@OverrideSystem.out.println("执行UserDao的add方法");System.out.println("执行UserService的update方法");接口依赖:JDK 动态代理强制要求目标类实现接口,CGlib 无此限制。性能:在多次调用场景下,CGlib 效率更高(因避免了反射开销)。限制:CGlib 无法代理 final 类或 final 方法(子类无法重写)。

2302_79926174的博客 847

动态代理:JDK代理原理解析

概要 代理模式结构   代理可分为2类对象:   目标类:一个是真正的你要访问的对象,原来的逻辑实现者。   代理类:一个是代理对象,从抽象层看,他包含了原来的逻辑与需要额外增加的代理/增强逻辑。   真正对象与代理对象实现同一个接口(实际上也可能是继承Cglib),先访问代理类再访问真正要访问的对象。   代理模式分为静态代理、动态代理。     静态代理是由程...

NEO_Rat的专栏 548

jdk动态代理原理解析

原文地址:https://www.cnblogs.com/gonjan-blog/p/6685611.html 想要理解jdk动态代理原理,需要对反射有一定的了解,然后再去思考下面两个问题: 为什么是基于接口的? 动态生成的代理类是什么样子?是如何执行的? 现在有一个接口以及它的实现类。现在需要在执行每个方法前后打印一条日志。 package proxy; public interface Activity { void partake(); void reward(); }

qinshi965273101的专栏 293

aop实现原理 - JDK动态代理(实例+源码解析)

动态代理: jdk代理-基于接口代理 通过 类:java.lang.reflect.Proxy 生成动态代理类 实现 接口:InvocationHandler 只能基于接口进行动态代理 代码实现: 1、创建接口 public interface Subject { void request(); void hello(); } 2、创建目标对象 /** * @autho...

FightingITPanda的博客 409
上一篇: 线程池探索之扩展篇
下一篇: IDEA中的并发调试
冥oo迹
博客等级 码龄16年 1粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值