Java之动态代理实践

功能概述

  • Java的动态代理,是代理模式的具体实现,即为其他对象提供一个代理以控制对某个对象的访问。Java的动态代理主要涉及两个类:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler,内部使用了缓存机制和反射机制实现。

功能实践

场景1:为Map接口生成代理

用例代码

@Test
public void test_jdk_dynamic_proxy_v1() {Map mapProxyInstance = (Map) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class[] { Map.class },new MyInvocationHandler(new HashMap<>()));mapProxyInstance.put("hello", "world"); //为接口创建代理对象后,就可以直接操作接口中的方法了(使用向上转型)mapProxyInstance.get("hello");CharSequence csProxyInstance = (CharSequence) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),new Class[] { CharSequence.class },new MyInvocationHandler("Hello World"));csProxyInstance.charAt(0);csProxyInstance.length();
}// 自定义的调用处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Object result = method.invoke(target, args);System.out.println("执行方法:" + method.getName() + ",结果值:" + result);return result;}
}

运行结果

执行方法:put,结果值:null
执行方法:get,结果值:world
执行方法:charAt,结果值:H
执行方法:length,结果值:11

结果分析

  • 为Map接口创建代理对象。并自定义调用处理器MyInvocationHandler,在使用代理接口调用方法时,就会回调MyInvocationHandler的invoke方法,传入代理对象、调用的方法、参数列表等

场景2:为自定义接口生成代理

用例代码

public void test_jdk_dynamic_proxy_v2() throws IOException {IHelloProxy helloProxy = (IHelloProxy) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),new Class[]{IHelloProxy.class}, new MyInvocationHandler(new HelloProxyImpl())); //要指定接口的实现类,在回调处理器的invoke方式时,调用目标类对应方法helloProxy.setProxyName("hello");String proxyName = helloProxy.getProxyName("hello");System.out.println("结果值:" + proxyName);
}public interface IHelloProxy {String getProxyName(String name);void setProxyName(String name);
}public class HelloProxyImpl implements IHelloProxy {private String proxyName;private static final String PREFIX = "proxy_";@Overridepublic String getProxyName(String name) {return proxyName;}@Overridepublic void setProxyName(String name) {proxyName = PREFIX + name;}
}
  • 注明:该场景用到的MyInvocationHandler,在场景1中有声明。

运行结果

执行方法:setProxyName,结果值:null
执行方法:getProxyName,结果值:proxy_hello
结果值:proxy_hello

结果分析

  • 在创建代理对象时,指定代理的接口IHelloProxy,以及调用处理器MyInvocationHandler,调用处理器指定了接口实现类,即目标对象
  • 在调用接口方法时,即调用调用代理对象的方法,会回调InvocationHandler中的invoke方法,可在该方法中做增强处理

原理分析

Proxy#newProxyInstance创建代理对象分析】

public class Proxy implements java.io.Serializable {private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); //代码1)public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, intfs); // 代码2)/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}final Constructor<?> cons = cl.getConstructor(constructorParams);//...代码省略...return cons.newInstance(new Object[]{h}); // 代码3)} catch (IllegalAccessException|InstantiationException e) // ...代码省略...}}private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}return proxyClassCache.get(loader, interfaces); //代码4)}
}
  • 代码解析:
    • 代码1)处:WeakCache用来存储代理类的缓存,若无缓存,会通过ProxyClassFactory创建
    • 代码2)处:从缓存中查找代理类,若没有创建代理类
    • 代码3)处:找到代理类包含InvocationHandler为参数的构造器Constructor,通过反射机制创建代理对象
    • 代码4)处:从缓存中去查代理类

ProxyClassFactory#apply创建代理类分析

private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>>
{//...代码省略...@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);for (Class<?> intf : interfaces) {Class<?> interfaceClass = null;try {interfaceClass = Class.forName(intf.getName(), false, loader); // 代码1)} catch (ClassNotFoundException e) {}if (interfaceClass != intf) {throw new IllegalArgumentException(intf + " is not visible from class loader");}if (!interfaceClass.isInterface()) { // 代码2)throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}/** Verify that this interface is not a duplicate.*/if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}// ...代码省略.../** Generate the specified proxy class.*/byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); // 代码3)try {return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); //代码4)} catch (ClassFormatError e) {/** A ClassFormatError here means that (barring bugs in the* proxy class generation code) there was some other* invalid aspect of the arguments supplied to the proxy* class creation (such as virtual machine limitations* exceeded).*/throw new IllegalArgumentException(e.toString());}}
}
  • 代码分析:
    • 代码1)处:判断如是否能够通过当前加载器加载Class
    • 代码2)处:判断代理的是否是接口,不是接口不能代理
    • 代码3)处:产生代理类Class的字节数组
    • 代码4)处:将字节数组通过native方法得到代理类的Class

功能总结

  • CGLIB与JDK动态代理区别
    • a)JDK代理只能对实现接口的类生成代理;CGLib是针对类实现代理
    • b)JDK代理使用的是反射机制实现aop的动态代理,CGLib代理使用字节码处理框架ASM
    • c)JDK动态代理机制是委托机制,CGLib则使用的继承机制

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apache Poi 实现Excel多级联动下拉框

由于最近做的功能&#xff0c;需要将接口返回的数据列表&#xff0c;输出到excel中&#xff0c;以供后续导入&#xff0c;且网上现有的封装&#xff0c;使用起来都较为麻烦&#xff0c;故参考已有做法封装了工具类。 使用apache poi实现excel联动下拉框思路 创建隐藏单元格&a…

如何评估开源项目的活跃度和可持续性?

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

元矿山下的音视频应用

// 近年来&#xff0c;矿业的技术和管理模式随着元宇宙的火爆和自动驾驶技术的发展逐渐变化、升级&#xff0c;进而衍生出元矿山的概念&#xff0c;音视频技术也在其中成为了关键一环。LiveVideoStackCon 2023 上海站邀请了来自希迪智驾的任思亮&#xff0c;为大家分享希迪智…

windows MFC消息demo

1、在BEGIN_MESSAGE_MAP添加MAP映射 BEGIN_MESSAGE_MAP(CdesktopTotalMapDlg, CDialogEx)ON_MESSAGE(WM_SYS_NORMAL_MSG, OnSysNormalMessage)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_SELECTSOURCEDIR, &CdesktopTotalMapDlg::OnB…

基于神经网络的3D地质模型

地球科学家需要对地质环境进行最佳估计才能进行模拟或评估。 除了地质背景之外&#xff0c;建立地质模型还需要一整套数学方法&#xff0c;如贝叶斯网络、协同克里金法、支持向量机、神经网络、随机模型&#xff0c;以在钻井日志或地球物理信息确实稀缺或不确定时定义哪些可能是…

机器学习笔记之优化算法(十九)经典牛顿法的收敛性分析

机器学习笔记之优化算法——经典牛顿法的收敛性分析 引言回顾&#xff1a;算法的收敛性分析 Wolfe \text{Wolfe} Wolfe准则的收敛性分析梯度下降法在凸函数的收敛性分析梯度下降法在强凸函数的收敛性分析 经典牛顿法的收敛性分析收敛性定理介绍证明过程关于隐含条件的说明 引言…

FFmpeg5.0源码阅读——FFmpeg大体框架

摘要&#xff1a;前一段时间熟悉了下FFmpeg主流程源码实现&#xff0c;对FFmpeg的整体框架有了个大概的认识&#xff0c;因此在此做一个笔记&#xff0c;希望以比较容易理解的文字描述FFmpeg本身的结构&#xff0c;加深对FFmpeg的框架进行梳理加深理解&#xff0c;如果文章中有…

Android Mvvm设计模式的详解与实战教程

一、介绍 在开发设计模式中&#xff0c;模式经历了多次迭代&#xff0c;从MVC到MVP&#xff0c;再到如今的MVVM。发现的过程其实很简单&#xff0c;就是为了项目更好的管理。 设计模式严格来说属于软件工程的范畴&#xff0c;但是如今在各大面试中或者开发中&#xff0c;设计模…

三方接口调用设计方案

在为第三方系统提供接口的时候&#xff0c;肯定要考虑接口数据的安全问题&#xff0c;比如数据是否被篡改&#xff0c;数据是否已经过时&#xff0c;数据是否可以重复提交等问题 在设计三方接口调用的方案时&#xff0c;需要考虑到安全性和可用性。以下是一种设计方案的概述&a…

【跟小嘉学 Rust 编程】十七、面向对象语言特性

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…

C# 学习笔记--个人学习使用 <2>

C# 学习笔记 Chapter 2 比较硬的基础部分Section 1 委托Part 1 Action 与 func 委托的示例Part 2 自定义委托Part 3 委托的一般使用Part 4 委托的高级使用Part 5 适时地使用接口 Interface 取代一些对委托的使用 Section 2 事件Part 1 初步了解事件Part 2 事件的应用Part 3 事件…

【Luniux】解决Ubuntu外接显示器不显示的问题

Luniux】解决Ubuntu外接显示器不显示的问题 文章目录 Luniux】解决Ubuntu外接显示器不显示的问题1. 检查nvidia显卡驱动是否正常2. 更新驱动3. 检查显示器是否能检测到Reference 1. 检查nvidia显卡驱动是否正常 使用命令行 nvidia-smi来检查显卡驱动是否正常&#xff0c;如果…

持续集成与持续交付:现代软件测试的变革之路

引言 在数字化时代&#xff0c;软件开发的速度和复杂性都在不断增加。为了满足市场的需求&#xff0c;企业需要更快、更高效地交付高质量的软件产品。在这样的背景下&#xff0c;持续集成与持续交付&#xff08;CI/CD&#xff09;成为了软件开发和测试的核心实践。 软件开发的…

论文阅读 The Power of Tiling for Small Object Detection

The Power of Tiling for Small Object Detection Abstract 基于深度神经网络的技术在目标检测和分类方面表现出色。但这些网络在适应移动平台时可能会降低准确性&#xff0c;因为图像分辨率的增加使问题变得更加困难。在低功耗移动设备上实现实时小物体检测一直是监控应用的…

小研究 - Java虚拟机性能及关键技术分析

利用specJVM98和Java Grande Forum Benchmark suite Benchmark集合对SJVM、IntelORP,Kaffe3种Java虚拟机进行系统测试。在对测试结果进行系统分析的基础上&#xff0c;比较了不同JVM实现对性能的影响和JVM中关键模块对JVM性能的影响&#xff0c;并提出了提高JVM性能的一些展望。…

css之文字连续光影特效、动画、scss

文章目录 效果图htmlscsscss 效果图 html <div><span>C</span><span>O</span><span>L</span><span>O</span><span>R</span><span>F</span><span>U</span><span>L</span&…

WOFOST模型与PCSE模型应用

实现作物产量的准确估算对于农田生态系统响应全球变化、可持续发展、科学粮食政策制定、粮食安全维护都至关重要。传统的经验模型、光能利用率模型等估产模型原理简单&#xff0c;数据容易获取&#xff0c;但是作物生长发育非常复杂&#xff0c;中间涉及众多生理生化过程&#…

Java学数据结构(2)——树Tree 二叉树binary tree 二叉查找树 AVL树 树的遍历

目录 引出什么是树Tree&#xff1f;树的实现二叉树binary tree查找树ADT——二叉查找树Binary Search Tree1.contains方法2.findMax和findMin方法3.insert方法4.remove方法&#xff08;复杂&#xff09;二叉查找树的深度 AVL(Adelson-Velskii和Landis)树——平衡条件(balance c…

流处理详解

【今日】 目录 一 Stream接口简介 Optional类 Collectors类 二 数据过滤 1. filter()方法 2.distinct()方法 3.limit()方法 4.skip()方法 三 数据映射 四 数据查找 1. allMatch()方法 2. anyMatch()方法 3. noneMatch()方法 4. findFirst()方法 五 数据收集…