JDK动态代理的实现及原理

Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理 上面的代码表明,首先通过getProxyClass获得这个代理类,然后通过c1.getConstructor()拿到构造函数,最后一步,通过cons.newInstance返回这个新的代理类的一个实例,注意:调用newInstance的时候,传入的参数为h,即我们自己定义好的InvocationHandler类,先记着这一步,后面我们就知道这里这样做的原因。也就是说,JDK的动态代理,是通过一个叫Proxy的类来实现的,我们继续跟进去,看看Proxy类的newProxyInstance()方法。 阅读详情

动态代理,听上去很高大上的技术,在Java里应用广泛,尤其是在Hibernate和Spring这两种框架里,在AOP,权限控制,事务管理等方面都有动态代理的实现。JDK本身有实现动态代理技术,但是略有限制,即被代理的类必须实现某个接口,否则无法使用JDK自带的动态代理,因此,如果不满足条件,就只能使用另一种更加灵活,功能更加强大的动态代理技术—— CGLIB。Spring里会自动在JDK的代理和CGLIB之间切换,同时我们也可以强制Spring使用CGLIB。下面我们就动态代理方面的知识点从头至尾依次介绍一下。

我们先来看一个例子:

新建一个接口,UserService.java, 只有一个方法add()。

package com.adam.java.basic;

public interface UserService {

public abstract void add();

}

建一个该接口的实现类UserServiceImpl.java

package com.adam.java.basic;

public class UserServiceImpl implements UserService {

@Override

public void add() {

System.out.println("----- add -----");

}

}

建一个代理处理类MyInvocationHandler.java

package com.adam.java.basic;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {

private Object target;

public MyInvocationHandler(Object target) {

super();

this.target = target;

}

public Object getProxy() {

return Proxy.newProxyInstance(Thread.currentThread()

.getContextClassLoader(), target.getClass().getInterfaces(),

this);

}

@Override

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

throws Throwable {

System.out.println("----- before -----");

Object result = method.invoke(target, args);

System.out.println("----- after -----");

return result;

}

}

测试类

package com.adam.java.basic;

public class DynamicProxyTest {

public static void main(String[] args) {

UserService userService = new UserServiceImpl();

MyInvocationHandler invocationHandler = new MyInvocationHandler(

userService);

UserService proxy = (UserService) invocationHandler.getProxy();

proxy.add();

}

}

执行测试类,得到如下输出:

----- before -----

----- add -----

----- after -----

到这里,我们应该会想到点问题:

1. 这个代理对象是由谁且怎么生成的?

2. invoke方法是怎么调用的?

3. invoke和add方法有什么对应关系?

4. 生成的代理对象是什么样子的?

带着这些问题,我们看一下源码。首先,我们的入口便是上面测试类里的getProxy()方法,我们跟进去,看看这个方法:

public Object getProxy() {

return Proxy.newProxyInstance(Thread.currentThread()

.getContextClassLoader(), target.getClass().getInterfaces(),this);

}

也就是说,JDK的动态代理,是通过一个叫Proxy的类来实现的,我们继续跟进去,看看Proxy类的newProxyInstance()方法。先来看看JDK的注释:

/**

* Returns an instance of a proxy class for the specified interfaces

* that dispatches method invocations to the specified invocation

* handler.

*

* <p>{@code Proxy.newProxyInstance} throws

* {@code IllegalArgumentException} for the same reasons that

* {@code Proxy.getProxyClass} does.

*

* @param loader the class loader to define the proxy class

* @param interfaces the list of interfaces for the proxy class

* to implement

* @param h the invocation handler to dispatch method invocations to

* @return a proxy instance with the specified invocation handler of a

* proxy class that is defined by the specified class loader

* and that implements the specified interfaces

根据JDK注释我们得知,newProxyInstance方法最终将返回一个实现了指定接口的类的实例,其三个参数分别是:ClassLoader,指定的接口及我们自己定义的InvocationHandler类。我摘几条关键的代码出来,看看这个代理类的实例对象到底是怎么生成的。

Class<?> cl = getProxyClass0(loader, intfs);

...

final Constructor<?> cons = cl.getConstructor(constructorParams);

...

return cons.newInstance(new Object[]{h});

有兴趣的同学可以自己看看JDK的源码,当前我用的版本是JDK1.8.25,每个版本实现方式可能会不一样,但基本一致,请研究源码的同学注意这一点。上面的代码表明,首先通过getProxyClass获得这个代理类,然后通过c1.getConstructor()拿到构造函数,最后一步,通过cons.newInstance返回这个新的代理类的一个实例,注意:调用newInstance的时候,传入的参数为h,即我们自己定义好的InvocationHandler类,先记着这一步,后面我们就知道这里这样做的原因。

其实这三条代码,核心就是这个getProxyClass方法,另外两行代码是Java反射的应用,和我们当前的兴趣点没什么关系,所以我们继续研究这个getProxyClass方法。这个方法,注释很简单,如下:

/*

* Look up or generate the designated proxy class.

*/

Class<?> cl = getProxyClass0(loader, intfs);

就是生成这个关键的代理类,我们跟进去看一下。

private static Class<?> getProxyClass0(ClassLoader loader,

Class<?>... interfaces) {

if (interfaces.length > 65535) {

throw new IllegalArgumentException("interface limit exceeded");

}

// If the proxy class defined by the given loader implementing

// the given interfaces exists, this will simply return the cached copy;

// otherwise, it will create the proxy class via the ProxyClassFactory

return proxyClassCache.get(loader, interfaces);

}

这里用到了缓存,先从缓存里查一下,如果存在,直接返回,不存在就新创建。在这个get方法里,我们看到了如下代码:

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

此处提到了apply(),是Proxy类的内部类ProxyClassFactory实现其接口的一个方法,具体实现如下:

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);

for (Class<?> intf : interfaces) {

/*

* Verify that the class loader resolves the name of this

* interface to the same Class object.

*/

Class<?> interfaceClass = null;

try {

interfaceClass = Class.forName(intf.getName(), false, loader);

} catch (ClassNotFoundException e) {

}

if (interfaceClass != intf) {

throw new IllegalArgumentException(

intf + " is not visible from class loader");

}...

看到Class.forName()的时候,我想大多数人会笑了,终于看到熟悉的方法了,没错!这个地方就是要加载指定的接口,既然是生成类,那就要有对应的class字节码,我们继续往下看:

/*

* Generate the specified proxy class.

*/

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

proxyName, interfaces, accessFlags);

try {

return defineClass0(loader, proxyName,

proxyClassFile, 0, proxyClassFile.length);

这段代码就是利用ProxyGenerator为我们生成了最终代理类的字节码文件,即getProxyClass0()方法的最终返回值。所以让我们回顾一下最初的四个问题:

1. 这个代理对象是由谁且怎么生成的?

2. invoke方法是怎么调用的?

3. invoke和add方法有什么对应关系?

4. 生成的代理对象是什么样子的?

对于第一个问题,我想答案已经清楚了,我再屡一下思路:由Proxy类的getProxyClass0()方法生成目标代理类,然后拿到该类的构造方法,最后通过反射的newInstance方法,产生代理类的实例对象。

接下来,我们看看其他的三个方法,我想先从第四个入手,因为有了上面的生成字节码的代码,那我们可以模仿这一步,自己生成字节码文件看看,所以,我用如下代码,生成了这个最终的代理类。

package com.adam.java.basic;

import java.io.FileOutputStream;

import java.io.IOException;

import sun.misc.ProxyGenerator;

public class DynamicProxyTest {

public static void main(String[] args) {

UserService userService = new UserServiceImpl();

MyInvocationHandler invocationHandler = new MyInvocationHandler(

userService);

UserService proxy = (UserService) invocationHandler.getProxy();

proxy.add();

String path = "C:/$Proxy0.class";

byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",

UserServiceImpl.class.getInterfaces());

FileOutputStream out = null;

try {

out = new FileOutputStream(path);

out.write(classFile);

out.flush();

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

out.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

上面测试方法里的proxy.add(),此处的add()方法,就已经不是原始的UserService里的add()方法了,而是新生成的代理类的add()方法,我们将生成的$Proxy0.class文件用jd-gui打开,我去掉了一些代码,add()方法如下:

public final void add()

throws

{

try

{

this.h.invoke(this, m3, null);

return;

}

catch (Error|RuntimeException localError)

{

throw localError;

}

catch (Throwable localThrowable)

{

throw new UndeclaredThrowableException(localThrowable);

}

}

核心就在于this.h.invoke(this. m3, null);此处的h是啥呢?我们看看这个类的类名:

public final class $Proxy0 extends Proxy implements UserService

不难发现,新生成的这个类,继承了Proxy类实现了UserService这个方法,而这个UserService就是我们指定的接口,所以,这里我们基本可以断定,JDK的动态代理,生成的新代理类就是继承了Proxy基类,实现了传入的接口的类。那这个h到底是啥呢?我们再看看这个新代理类,看看构造函数:

public $Proxy0(InvocationHandler paramInvocationHandler)

throws

{

super(paramInvocationHandler);

}

构造函数里传入了一个InvocationHandler类型的参数,看到这里,我们就应该想到之前的一行代码:

return cons.newInstance(new Object[]{h});

这是newInstance方法的最后一句,传入的h,就是这里用到的h,也就是我们最初自己定义的MyInvocationHandler类的实例。所以,我们发现,其实最后调用的add()方法,其实调用的是MyInvocationHandler的invoke()方法。我们再来看一下这个方法,找一下m3的含义,继续看代理类的源码:

static

{

try

{

m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

m3 = Class.forName("com.adam.java.basic.UserService").getMethod("add", new Class[0]);

m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

return;

}

惊喜的发现,原来这个m3,就是原接口的add()方法,看到这里,还有什么不明白的呢?我想2,3,4问题都应该迎刃而解了吧?我们继续,看看原始MyInvocationHandler里的invoke()方法:

@Override

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

throws Throwable {

System.out.println("----- before -----");

Object result = method.invoke(target, args);

System.out.println("----- after -----");

return result;

}

m3就是将要传入的method,所以,为什么先输出before,后输出after,到这里是不是全明白了呢?这,就是JDK的动态代理整个过程,不难吧?

最后,我稍微总结一下JDK动态代理的操作过程:

1. 定义一个接口,该接口里有需要实现的方法,并且编写实际的实现类。

2. 定义一个InvocationHandler类,实现InvocationHandler接口,重写invoke()方法,且添加getProxy()方法。

总结一下动态代理实现过程:

1. 通过getProxyClass0()生成代理类。

2. 通过Proxy.newProxyInstance()生成代理类的实例对象,创建对象时传入InvocationHandler类型的实例。

3. 调用新实例的方法,即此例中的add(),即原InvocationHandler类中的invoke()方法。

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

相关推荐

华为云HCS解决方案笔记HUAWEI CLOUD Stack【面试篇】

华为云解决方案介绍、华为云服务介绍,华为云网络平面规划等

weixin_48375618的博客 1万+

动态代理(Proxy)类和类加载器

动态代理(proxy)类和类加载器 作者:zhrb 学习该技术之前,需要对反射Method对象的使用有一定的了解。 可以在运行时创建一个实现了一组给定接口的新类(代理类)。不过该代理类和Hibernate中使用的代理类使用的是不一样的技术。 该代理类具有下列方法:  指定接口所需要的全部方法  Object类中的全部方法 但是该代理类不能定义其所实现接口的方法,即,不能编...

zhrb 1193

tkinter表格进阶:5分钟实现CSV数据可视化编辑界面(含右键菜单配置)

本文详细介绍了如何使用Python的tkintertable库快速构建CSV数据可视化编辑界面。通过导入导出CSV数据、深度定制右键菜单(如复制、插入行、删除行)以及实现单元格验证等进阶功能,开发者能在5分钟内创建一个可嵌入自动化流程的专业级桌面数据编辑工具,显著提升数据处理效率。

931

Java进阶学习第24天——动态代理与类加载器

动态代理 代理模式 AOP:面向切面的编程 动态代理实现 动态代理案例1—-实现编码过滤 动态代理案例2—细粒度的权限控制 类加载器 全盘负责委托机制 自定义类加载器 泛型反射

lutianfei的专栏 7693

Mamba与iTransformer融合:时间序列预测中的曼巴注意力机制创新实践

本文探讨了Mamba与iTransformer融合在时间序列预测中的创新实践,重点介绍了曼巴注意力机制的设计与优势。通过双路径信息流设计,该方案在保持O(n)复杂度的同时,显著提升了预测精度和训练效率。实验证明,混合架构在电力负荷、气象数据等多变量长序列场景中表现优异,预测误差降低至0.083,内存占用仅4.2GB。

weixin_29292535的博客 220

高效打卡系统设计:从行为心理学到实践工具

打卡系统作为现代时间管理的重要工具,其核心原理植根于行为心理学中的即时反馈机制和习惯养成理论。通过科学设计的打卡周期和可视化反馈,能有效提升个人自律性和目标达成率。在技术实现上,从纸质记录到数字化工具(如Notion、Excel自动化模板)的演进,为不同场景提供了灵活解决方案。特别在习惯追踪和进度管理领域,结合热力图生成、数据统计等技巧,打卡系统能转化为强大的自我监督工具。本文通过实践案例,详解如何构建兼顾弹性和约束力的个性化打卡方案,帮助读者突破'三天打鱼两天晒网'的常见困境。

weixin_30047059的博客 341

jdk动态代理原理分析

目录一、为什么要用代理二、使用jdk代理三、jdk动态代理实现原理分析 一、为什么要用代理 代理就是控制目标对象的访问;那么为什么需要代理呢? 假设第一个场景: 系统用户有2种,分别是游客用户和正式用户;必然的游客用户有些功能是不能用的,对应的就是后台某些方法是不能给他调用的;如果是在方法内部加判断的话就比较费力,当然这是一个解决方案;不过这种实现并不优雅。用过spring aop的都知道可以用环绕来解决这个问题,spring aop原理就是使用代理。 第二个场景: 比如一个RPC框架,我本地语义

愤怒的苹果ext的博客 608

MATLAB创建GUI面板实现二维码生成与识别

利用matlab设计gui界面实现二维码的生成与内容识别

infinitystardust的博客 3150

Java中JDK动态代理详解

类加载器 ,因为我们生成的这个代理对象是没有.java源文件的,是在运行时直接生成的一个.class的二进制字节码文件,他既然直接生成一个.class二进制文件的,那么它是需要去加载,被加载到Java的运行时数据区,所以他就需要一个类加载器。类加载器还有另一个重要的功能,那就是隔离加载类的命名空间。不同的类加载器可以加载同名的类,但是由不同的类加载器加载的类属于不同的命名空间,互不干扰。它不仅负责加载类,还负责对类进行校验,解析和初始化,以及隔离加载类的命名空间。:当前的方法中,我要增强的那个方法。

Upup! 1458

PySWMM新手避坑指南:从安装到批量处理INP文件的完整流程(附常见错误解决方案)

本文为PySWMM新手提供从环境搭建到批量处理INP文件的完整避坑指南。详细讲解了虚拟环境配置、库安装方法,并针对“无法打开输入文件”和对象访问错误等高频问题提供解决方案。核心部分展示了如何利用Python脚本实现INP文件的批量运行、结果汇总以及参数自动化修改,显著提升SWMM模型的工作效率。

weixin_29092031的博客 393

零基础入门simulink仿真--通信领域的调制解调技术:基于Simulink的PM信号调制与解调仿真建模示例

PM的基本原理是将基带信号(如语音或音乐信号)加载到高频载波信号的相位上。通过改变载波的瞬时相位,可以实现信息的传输。PM的优点包括抗噪声能力强、频谱利用率较高等,但其解调过程相对复杂。通过Simulink,我们可以通过模块化的建模方式实现PM信号的调制和解调过程,并观察信号在信道中的传输效果。通过本指南,我们介绍了如何基于Simulink搭建一个完整的PM信号调制与解调仿真模型,并进行仿真和性能评估。背景介绍:理解PM信号调制与解调的基本原理及其应用。所需工具和环境:列出进行PM仿真所需的工具和环境。

amy_mhd的博客 607

Java JDK 动态代理(AOP)使用及实现原理分析

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息 ​ 一、什么是代理? 二、Java 动态代理类 三、JDK的动态代理怎么使用? 四、动态代理怎么实现的? 五、结论 一、什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。 代理模式UML图: 简单结构示意图: 为了保持行为的...

勿忘初心 16万+

智能车浅谈——手把手让车跑起来(电磁篇)

手把手让你的电磁车上路。

qq_41954556的博客 7万+

【JDK动态代理实现原理详解】

首先,我们需要定义一个包含我们想要代理的方法的接口。例如,我们可以定义一个。

qq_33545255的博客 1264

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

JDK动态代理的底层实现原理

代理模式定义 存在一个代理对象,并且这个代理对象持有真实对象的引用,以实现对真实对象的访问控制。 举个例子,现在公司一般都有VPN,使我们在家也能访问到公司的内网(比如连接公司的数据库等),实现居家办公。这里VPN相当于一个代理,而公司内网相当于被代理对象,也就是真实对象。我们不能直接访问公司内网(真实对象),但是我们通过VPN(代理对象),输入身份信息,确认无误后就可以访问到公司内网。这就是

至道 8492

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

细说JDK动态代理的实现原理

关于JDK的动态代理,最为人熟知的可能要数Spring AOP的实现,默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。那么,什么是JDK的动态代理呢? JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。JDK从1.3开始支持动态代理。那么JDK是如何生成动态代理的

痴人说梦 5万+

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

JDK动态代理实现原理

JDK动态代理实现原理 动态代理机制 通过实现 InvocationHandler 接口创建自己的调用处理器 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入 Interface Inv...

java界的小学生 334

JDK动态代理实现原理及手写JDK动态代理

1.JDK动态代理原理分析 在使用jdk动态代理的时候,必须要实现InvocationHandler接口,invoke方法Invoke 方法中该三个参数分别表示为:代理对象、被代理执行的方法、参数 使用jdk动态代理获取代理类对象(JDK自动生成代理类)$Proxy0.class 将生成代理类加载至内存,让该类变成可执行类 2.源码分析 这里查看JDK1.8.0_191...

qq_36617322的博客 465

JDK动态代理的实现原理

JDK动态代理的实现原理 转载:By:芦青 1.引言 在开发中过程中,我们经常性的会对一个对象的方法进行增强,当然如果要增强一个对象的方法,我们会有很多种方式, 那么动态代理肯定是其中的一种方式,但是很多情况下,我们只会去使用动态代理,对动态代理的实现原理并不是很清晰,那这里我们就可以来聊一聊JDK动态代理的底层原理 2.需求 首先我们先有一个接口叫UserService,接口中有一个save的保...

xc965746550的博客 266

JDK动态代理实现原理以及手写实现JDK动态代理

JDK动态代理是采用字节重组,重新生成对象来替代原始对象,以达到动态代理的目的。在理清楚JDK动态代理的过程后,手写实现JDK动态代理过程。

积跬步,至千里。 868

JDK动态代理 和CGLib动态代理实现原理和两者的区别

JDK动态代理和CGLib动态代理

zeng2813的博客 1091

JDK与CGLib动态代理实现原理

代理模式;jdk实现动态代理;CGLib实现动态代理;jdk与CGLib的区别

LPClan的博客 2062

【Java】JDK动态代理实现原理

JDK的动态代理实现原理是在运行中动态生成代理类,这个代理类实现了Subject接口,在对代理类进行实例化的时候,需要传入InvocationHandler,当调用代理类的方法时,会执行InvocationHandler的invoke方法,在invoke方法中再执行真正的目标方法,从而完成代理功能。

S-H_A-N 393

JDK动态代理的实现原理浅析

JDK动态代理简介 什么是JDK动态代理? 先看看代理的概念——代理:为其他对象提供一种代理以控制对这个对象的访问。 代理在生活中很常见,比如买火车票可以不去火车站,而是通过代售点;想要逃课了,就让同学代签到等等。 说白了,就是可以通过代理来完成目标事件。(自我理解) 不同于静态代理直接采用编码的方式实现,JDK动态代理是利用反射机制在运行时创建代理类,进而调用相应的方法。 J...

babylove_BaLe的博客 649
上一篇: atomic包的原理及分析
下一篇: Hadoop原理及架构
java1856905
博客等级 码龄8年 100粉丝 249原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值