(Dynamic Proxy)动态代理模式的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类中代码很繁杂,人家一看就不想要使用动态代理了,认为一使用就要有相当的消耗。那么此时我们可以再继续扩展一下,设计一个类能够根据传进的相关参数而返回最终客户需要的代理,这样的类设计是不是很像前面文章中工厂方法模式的应用呢?
 
   花了快一个钟之后加上这个时序图,因为对此不甚了解,应该有许多错误,望各位指正,谢谢!


本文转自 xxxx66yyyy 51CTO博客,原文链接:http://blog.51cto.com/haolloyin/333257,如需转载请自行联系原作者
相关文章
|
5天前
|
Java API 开发者
解密Java反射机制与动态代理
解密Java反射机制与动态代理
10 0
|
7天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
74 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
12天前
|
设计模式 前端开发 Java
19:Web开发模式与MVC设计模式-Java Web
19:Web开发模式与MVC设计模式-Java Web
22 4
|
15天前
|
设计模式 Java 测试技术
滚雪球学Java(25):动态代理
【4月更文挑战第14天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
13 0
滚雪球学Java(25):动态代理
|
16天前
|
Java 开发者 UED
Java 异步和事件驱动编程:探索响应式模式
【4月更文挑战第27天】在现代软件开发中,异步和事件驱动编程是提高应用性能和响应性的关键策略。Java 提供了多种机制来支持这些编程模式,使开发者能够构建高效、可扩展的应用程序。
30 4
|
16天前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
37 2
|
17天前
|
人工智能 监控 数据可视化
Java智慧工地云平台源码带APP SaaS模式 支持私有化部署和云部署
智慧工地是指应用智能技术和互联网手段对施工现场进行管理和监控的一种工地管理模式。它利用传感器、监控摄像头、人工智能、大数据等技术,实现对施工现场的实时监测、数据分析和智能决策,以提高工地的安全性、效率和质量(技术架构:微服务+Java+Spring Cloud +UniApp +MySql)。
36 4
|
19天前
|
监控 Java 数据安全/隐私保护
Java中的动态代理
Java中的动态代理
|
19天前
|
人工智能 监控 安全
JAVA基于SaaS模式的智慧工地云平台源码(云智慧工地解决方案)
智慧工地支持多端展示(PC端、手机端、平板端)SaaS微服务架构,项目监管端,工地管理端源码
22 0
|
19天前
|
设计模式 缓存 算法
Java 的静态代理和动态代理
Java 的静态代理和动态代理
11 0