(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,如需转载请自行联系原作者
相关文章
|
3月前
|
Java Spring 数据库连接
[Java]代理模式
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
62 0
[Java]代理模式
|
2月前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
3月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
99 1
|
3月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
3月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
4月前
|
存储 Java 开发者
【Java新纪元启航】JDK 22:解锁未命名变量与模式,让代码更简洁,思维更自由!
【9月更文挑战第7天】JDK 22带来的未命名变量与模式匹配的结合,是Java编程语言发展历程中的一个重要里程碑。它不仅简化了代码,提高了开发效率,更重要的是,它激发了我们对Java编程的新思考,让我们有机会以更加自由、更加创造性的方式解决问题。随着Java生态系统的不断演进,我们有理由相信,未来的Java将更加灵活、更加强大,为开发者们提供更加广阔的舞台。让我们携手并进,共同迎接Java新纪元的到来!
91 11
|
5月前
|
消息中间件 Java
【实战揭秘】如何运用Java发布-订阅模式,打造高效响应式天气预报App?
【8月更文挑战第30天】发布-订阅模式是一种消息通信模型,发送者将消息发布到公共队列,接收者自行订阅并处理。此模式降低了对象间的耦合度,使系统更灵活、可扩展。例如,在天气预报应用中,`WeatherEventPublisher` 类作为发布者收集天气数据并通知订阅者(如 `TemperatureDisplay` 和 `HumidityDisplay`),实现组件间的解耦和动态更新。这种方式适用于事件驱动的应用,提高了系统的扩展性和可维护性。
89 2
|
4月前
|
设计模式 Java
Java设计模式-工厂方法模式(4)
Java设计模式-工厂方法模式(4)
|
5月前
|
设计模式 XML 存储
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
文章详细介绍了工厂方法模式(Factory Method Pattern),这是一种创建型设计模式,用于将对象的创建过程委托给多个工厂子类中的某一个,以实现对象创建的封装和扩展性。文章通过日志记录器的实例,展示了工厂方法模式的结构、角色、时序图、代码实现、优点、缺点以及适用环境,并探讨了如何通过配置文件和Java反射机制实现工厂的动态创建。
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
|
5月前
|
设计模式 XML Java
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)
文章详细介绍了简单工厂模式(Simple Factory Pattern),这是一种创建型设计模式,用于根据输入参数的不同返回不同类的实例,而客户端不需要知道具体类名。文章通过图表类的实例,展示了简单工厂模式的结构、时序图、代码实现、优缺点以及适用环境,并提供了Java代码示例和扩展应用,如通过配置文件读取参数来实现对象的创建。
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)