基于JDK的动态代理原理分析
基于JDK的动态代理原理分析
这篇文章解决三个问题:
What 动态代理是什么
How 动态代理怎么用
Why 动态代理的原理
动态代理是什么?
动态代理是代理模式的一种具体实现,是指在程序运行期间,动态的生成目标对象的代理类(直接加载在内存中的字节码文件),实现对目标对象所有方法的增强。通过这种方式,我们可以在不改变(或无法改变)目标对象源码的情况下,对目标对象的方法执行前后进行干预。
动态代理怎么用?
首先,准备好我们需要代理的类和接口,因为JDK的动态代理是基于接口实现的,所以被代理的对象必须要有接口。
/**
* SaySomething接口
*/
public interface SaySomething {
public void sayHello();
public void sayBye();
}
/**
* SaySomething的实现类
*/
public class SaySomethingImpl implements SaySomething {
@Override
public void sayHello() {
System.out.println("Hello World");
}
@Override
public void sayBye() {
System.out.println("Bye Bye");
}
}
按照动态代理的用法,需要自定义一个处理器,用来编写自定义逻辑,实现对被代理对象的增强。
自定义的处理器需要满足以下要求:
需要实现InvocationHandler,重写invoke方法,在invoke方法中通过加入自定义逻辑,实现对目标对象的增强。
需要持有一个成员变量,成员变量的是被代理对象的实例,通过构造参数传入。(用来支持反射调用被代理对象的方法)
需要提供一个参数为被代理对象接口类的有参构造。(用来支持反射调用被代理对象的方法)
/**
* 自定义的处理器,用来编写自定义逻辑,实现对被代理对象的增强
*/
public class CustomHandler implements InvocationHandler {
//需要有一个成员变量,成员变量为被代理对象,通过构造参数传入,用来支持方法的反射调用。
private SaySomething obj;
//需要有一个有参构造,通过构造函数将被代理对象的实例传入,用来支持方法的反射调用
public CustomHandler(SaySomething obj) {
this.obj = obj;
}
/**
* proxy:动态生成的代理类对象com.sun.proxy.$Proxy0
* method:被代理对象的真实的方法的Method对象
* args:调用方法时的入参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标方法执行前的自定义逻辑处理
System.out.println("-----before------");
//执行目标对象的方法,使用反射来执行方法,反射需要传入目标对象,此时用到了成员变量obj。
Object result = method.invoke(obj, args);
//目标方法执行后的自定义逻辑处理
System.out.println("-----after------");
return result;
}
}
这样我们就完成了自定义处理器的编写,同时在invoke方法中实现对了代理对象方法的增强,被代理类的所有方法的执行都会执行我们自定义的逻辑。
接下来,需要通过Proxy,newProxyInstance()方法来生成代理对象的实例,并进行方法调用测试。
public class JdkProxyTest {
public static void main(String[] args) {
//将生成的代理对象的字节码文件 保存到硬盘
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//被代理对象的实例
SaySomething obj = new SaySomethingImpl();
//通过构造函数,传入被代理对象的实例,生成处理器的实例
InvocationHandler handler = new CustomHandler(obj);
//通过Proxy.newProxyInstance方法,传入被代理对象Class对象、处理器实例,生成代理对象实例
SaySomething proxyInstance = (SaySomething) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
new Class[]{SaySomething.class}, handler);
//调用生成的代理对象的sayHello方法
proxyInstance.sayHello();
System.out.println("===================分割线==================");
//调用生成的代理对象的sayBye方法
proxyInstance.sayBye();
}
}

运行main方法,查看控制台,大功告成。至此,我们已经完整的完成了一次动态代理的使用。
动态代理的原理
生成的proxyInstance对象到底是什么,为什么调用它的sayHello方法会执行CustomerHandler的invoke方法呢?
直接贴上proxyInstance的字节码文件,我们就会恍然大悟了...
//$Proxy0是SaySomething的实现类,重写了sayHello和sayBye方法
public final class $Proxy0 extends Proxy implements SaySomething {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.example.demo.hanmc.proxy.jdk.SaySomething").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.example.demo.hanmc.proxy.jdk.SaySomething").getMethod("sayBye");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
//实现了接口的sayHello方法,在方法内部调用了CustomerHandler的invoke方法,同时传入了Method对象,
//所以在CustomerHandler对象中可以通过mathod.invovke方法调用SyaSomthing的sayHello方法
public final void sayHello() throws {
try {
//h是父类Proxy中的InvocationHandler对象,其实就是我们自定义的CustomHandler对象
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sayBye() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
//忽略内容
}
public final boolean equals(Object var1) throws {
//忽略内容
}
public final String toString() throws {
//忽略内容
}
}
看到了生成的代理对象的字节码文件,是不是一切都明白你了,原理竟然如此简单^_^
本文为个人学习整理,如有描述错误或者对相关内容感兴趣,欢迎评论或私信交流,一起讨论、共同进步。
基于JDK的动态代理原理分析的更多相关文章
- 基于 JDK 的动态代理机制
『动态代理』其实源于设计模式中的代理模式,而代理模式就是使用代理对象完成用户请求,屏蔽用户对真实对象的访问. 举个最简单的例子,比如我们想要「FQ」访问国外网站,因为我们并没有墙掉所有国外的 IP,所 ...
- 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)
代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...
- 设计模式学习——JAVA动态代理原理分析
一.JDK动态代理执行过程 上一篇我们讲了JDK动态代理的简单使用,今天我们就来研究一下它的原理. 首先我们回忆下上一篇的代码: public class Main { public static v ...
- cglib源码分析(四):cglib 动态代理原理分析
本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...
- 设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理
本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网 ...
- JDK和CGLIB动态代理原理
1.JDK动态代理利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理. 2.CGLiB动态代 ...
- 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理
本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...
- JDK和CGLIB动态代理原理区别
JDK和CGLIB动态代理原理区别 https://blog.csdn.net/yhl_jxy/article/details/80635012 2018年06月09日 18:34:17 阅读数:65 ...
- Java设计模式之JDK动态代理原理
动态代理核心源码实现public Object getProxy() { //jdk 动态代理的使用方式 return Proxy.newProxyInstance( this.getClass(). ...
随机推荐
- Solution -「洛谷 P6158」封锁
\(\mathcal{Description}\) Link. 给定一个 \(n\times n\) 的格点图,横纵相邻的两格点有一条边权为二元组 \((w,e)\) 的边.求对于 \(S=( ...
- 利用 kubeasz 给 suse 12 部署 kubernetes 1.20.1 集群
文章目录 1.前情提要 2.环境准备 2.1.环境介绍 2.2.配置静态网络 2.3.配置ssh免密 2.4.批量开启模块以及创建文件 2.5.安装ansible 2.5.1.安装pip 2.5.2. ...
- MXNet源码分析 | Gluon接口分布式训练流程
本文主要基于MXNet1.6.0版本,对Gluon接口的分布式训练过程进行简要分析. 众所周知,KVStore负责MXNet分布式训练过程中参数的同步,那么它究竟是如何应用在训练中的呢?下面我们将从G ...
- Spring Security配置个过滤器也这么卷
以前胖哥带大家用Spring Security过滤器实现了验证码认证,今天我们来改良一下验证码认证的配置方式,更符合Spring Security的设计风格,也更加内卷. CaptchaAuthent ...
- [LeetCode]1108. IP 地址无效化
给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本. 所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 ".". 示例 ...
- Eclipse插件拓展点
一.新建一个项目,不使用模板 二.增加"hello"拓展点 1. 打开插件描述文件的Extensions页 新建一个插件后,会自动打开插件清单文件编辑器,也可以通过META-INF ...
- DHCPv4协议测试——信而泰网络测试仪实操
一.DHCP简介 1. DHCP原理 DHCPv4概述 上网最基本元素 · IP地址 · 子网掩码 · 缺省网关 · DNS服务器 DHCP概述-手工配置 为什么需要自动分配,手工配置不行吗? · 答 ...
- Zabbix——可视化的资源监控解决方案
Zabbix监控的内容 1.硬件监控 温度 磁盘.主板等故障 待机时间 2.系统监控 CPU 内存 负载 磁盘 内核参数 网卡流量 TCP连接数 进程数 IO 端口采集 3.程序 应用:Nginx.m ...
- k8s全方位监控中-常用rules配置
[root@VM_0_48_centos prometheus]# cat alertmanager-configmap.yaml apiVersion: v1 kind: ConfigMap met ...
- BBS项目(一)
目录 BBS项目(一) 项目开发流程 BBS项目 BBS表分析 自关联 表关系图示 BBS项目(一) 项目开发流程 项目分类 针对互联网用户:抖音,淘宝····· 针对公司内部:后台管理系统··· 针 ...