动态代理模式的Java实现

2024-09-06 01:08
文章标签 java 动态 实现 模式 代理

本文主要是介绍动态代理模式的Java实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

动态代理(Dynamic Proxy):相比前一篇文章所实现的静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现
我们知道,所谓代理,就是需要代理类和被代理类有相同的对外接口或者说成服务,所以代理类一般都必须实现了所有被代理类已实现的接口,因为接口就是制定了一系列对外服务的标准。
正因为动态代理有这样灵活的特性,所以我们在设计动态代理类(DynamicProxy)时不用显式地让它实现与真实主题类(RealSubject)相同的接口(interface),而是把这种实现推迟到运行时。
为了能让DynamicProxy类能够在运行时才去实现RealSubject类已实现的一系列接口并执行接口中相关的方法操作,需要让DynamicProxy类实现JDK自带的java.lang.reflect.InvocationHandler接口,该接口中的invoke()方法能够让DynamicProxy实例在运行时调用被代理类的“对外服务”,即调用被代理类需要对外实现的所有接口中的方法,也就是完成对真实方法的调用,Java帮助文档中称这些真实方法为处理程序。
按照上面所述,我们肯定必须先把被代理类RealSubject已实现的所有interface都加载到JVM中,不然JVM怎么能够找到这些方法呢?明白了这个道理,那么我们就可以创建一个被代理类的实例,获得该实例的类加载器ClassLoader
所谓的类加载器ClassLoader,就是具有某个类的类定义,即类的内部相关结构(包括继承树、方法区等等)。
更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到控制被代理对象的行为的目的。请详看下面代码中的DynamicProxy类,其中必须实现的invoke()方法在调用被代理类的真实方法的前后都可进行一定的特殊操作。这是动态代理最明显的优点。

虽然都是根据自己看了书之后的理解说了这么多,不知道能不能让人明白,这里先给出动态代理的类图吧,如下:


具体代码实现如下:
  
  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;  
  4.  
  5. //抽象主题类,这里不能用abstract抽象类,一定要是interface  
  6. interface AbstractSubject {  
  7.     public abstract void request();  
  8. }  
  9.  
  10. // 真实主题类,即被代理类  
  11. class RealSubject implements AbstractSubject {  
  12.     public void request() {  
  13.         System.out.println("RealSubject's request() ...");  
  14.     }  
  15. }  
  16.  
  17. // 动态代理类,实现InvocationHandler接口  
  18. class DynamicProxy implements InvocationHandler {  
  19.  
  20.     // 被代理类的实例  
  21.     Object obj = null;  
  22.  
  23.     // 将被代理者的实例传进动态代理类的构造函数中  
  24.     public DynamicProxy(Object obj) {  
  25.         this.obj = obj;  
  26.     }  
  27.  
  28.     /**  
  29.      * 覆盖InvocationHandler接口中的invoke()方法  
  30.      *   
  31.      * 更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构  
  32.      * 的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到  
  33.      * 控制被代理对象的行为,下面的before、after就是我们可以进行特殊  
  34.      * 代码切入的扩展点了。  
  35.      */ 
  36.     public Object invoke(Object proxy, Method method, Object[] args)  
  37.             throws Throwable {  
  38.         /*  
  39.          * before :doSomething();  
  40.          */ 
  41.         Object result = method.invoke(this.obj, args);  
  42.           
  43.         /*  
  44.          * after : doSomething();  
  45.          */ 
  46.         return result;  
  47.     }  
  48. }  
  49.  
  50. // 测试类  
  51. public class Client {  
  52.     public static void main(String[] args) {  
  53.  
  54.         // 被代理类的实例  
  55.         AbstractSubject realSubject = new RealSubject();  
  56.  
  57.         // 获得被代理类的类加载器,使得JVM能够加载并找到被代理类的内部结构,以及已实现的interface  
  58.         ClassLoader loader = realSubject.getClass().getClassLoader();  
  59.  
  60.         // 获得被代理类已实现的所有接口interface,使得动态代理类的实例  
  61.         Class<?>[] interfaces = realSubject.getClass().getInterfaces();  
  62.  
  63.         // 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序  
  64.         InvocationHandler handler = new DynamicProxy(realSubject);  
  65.  
  66.         /*  
  67.          * loader : 被代理类的类加载器  
  68.          * interfaces :被代理类已实现的所有接口,而这些是动态代理类要实现的接口列表  
  69.          * handler : 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序  
  70.          *   
  71.          * return :返回实现了被代理类所实现的所有接口的Object对象,即动态代理,需要强制转型  
  72.          */ 
  73.         //获得代理的实例  
  74.         AbstractSubject proxy = (AbstractSubject) Proxy.newProxyInstance(  
  75.                 loader, interfaces, handler);  
  76.  
  77.         proxy.request();  
  78.         //打印出该代理实例的名称  
  79.         System.out.println(proxy.getClass().getName());  
  80.     }  
 
测试结果:
RealSubject's request() ...
DesignPattern.proxy.dynamicProxy.$Proxy0

运行结果与前一篇文章中静态代理的一样,我们也发现这个动态代理的实例的名称为“$Proxy0”,前面的都是我所用的包名,记得以前学习内部类时,内部类编译之后生成的.class文件的默认命名方式是带有“$”。但是现在这个肯定不是内部类,因为“$”之前并没有任何一个外部类的名称。是不是以后遇到“$Proxy0”这样的名字就可以推断出该实例一定是个动态代理类的实例呢?有待证明。
其实,在上面整个代码当中,注释最少的就是DynamicProxy类中的invoke()方法了,因为看了Java帮助文档也不是特别明白,所以不敢乱写,这一点还有待探究。
话又说回来,动态代理机制确实很灵活,或者说很智能,但是这是运用到了Java中的反射机制,而反射机制又与JVM中栈区、堆区、方法区等底层细节以及类的加载、生命周期等知识相关,要完全理解相当不容易,看来还有很长的路要走呢!
     最后,可能大家都觉得测试类即Client类中代码很繁杂,人家一看就不想要使用动态代理了,认为一使用就要有相当的消耗。那么此时我们可以再继续扩展一下,设计一个类能够根据传进的相关参数而返回最终客户需要的代理,这样的类设计是不是很像前面文章中工厂方法模式的应用呢?

这篇关于动态代理模式的Java实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1140571

相关文章

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.