代理模式(五)JDK动态代理深入分析

原文出处:http://blog.csdn.net/mark_lq/article/details/48178497

JDK动态代理实现的核心技术是java反射机制,其主要的两个类是:Proxy、InvocationHandler。使用java.lang.reflect.Proxy类可以动态实现接口作为代理类。

一. Creating Proxies

使用Proxy.newProxyInstance()方法创建动态代理类,方法定义:

[java]  view plain  copy
  1. public static Object newProxyInstance(ClassLoader loader,  
  2.                                       Class<?>[] interfaces,  
  3.                                       InvocationHandler h)  

  1. ClassLoader :加载动态代理类的类加载器
  2. interfaces   :该动态代理类所要实现的接口
  3. InvocationHandler:事件处理,转发所有的方法调用代理。
  4. 函数返回创建的代理类,实现了interfaces接口

例如:

[java]  view plain  copy
  1. InvocationHandler handler = new MyInvocationHandler();  
  2. MyInterface proxy = (MyInterface) Proxy.newProxyInstance(  
  3.                             MyInterface.class.getClassLoader(),  
  4.                             new Class[] { MyInterface.class },  
  5.                             handler);  

   函数返回值强转成MyInterface类型,所有对代理类的调用都将转发到实现了InvocationHandler的handler对象中。

二. InvocationHandler's

Proxy.newProxyInstance() 方法中需要传入实现InvocationHandler的对象,proxy方法的调用才能转发到InvocationHandler的实现对象中。

[java]  view plain  copy
  1. public interface InvocationHandler{  
  2.   Object invoke(Object proxy, Method method, Object[] args)  
  3.          throws Throwable;  
  4. }  
例如:

[java]  view plain  copy
  1. public class MyInvocationHandler implements InvocationHandler{  
  2.   
  3.   public Object invoke(Object proxy, Method method, Object[] args)  
  4.   throws Throwable {  
  5.     //do something "dynamic"  
  6.   }  
  7. }  

   1. proxy就是通过 Proxy.newProxyInstance()方法创建的动态代理类,它实现了指定的interfaces接口。 通常这个对象在invoke函数中不使用。

   2. Method 表示动态代理类所实现的接口中的方法。通过 Method 对象可以获取方法名、参数类型、返回类型等信息。

   3. Object[] args 包含了传入动态代理类所实现的方法的参数值。

三. JDK动态代理类Demo及原理分析

      Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口 , 客户端通常需要针对抽象主题角色进行编程(面向接口编程)。
[java]  view plain  copy
  1. package com.markliu.proxy;  
  2.   
  3. /** 
  4.  * 抽象角色;动态代理只能代理接口 
  5.  * @author markliu 
  6.  * 
  7.  */  
  8. public interface Subject {    
  9.     public void request();  
  10. }  

     DynamicInvocationHandler(代理主题中的业务处理角色):它包含了对真实主题的引用(对象组合),从而可以在invoke方法中操作真实主题对象;在代理主题角色中提供一个实现与真实主题角色相同接口的对象realSubject,以便在任何时候都可以替代真实主题;在代理主题角色中,invoke函数在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
[java]  view plain  copy
  1. package com.markliu.proxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5.   
  6. /** 
  7.  * 动态代理类 
  8.  * 实现了InvocationHandler 
  9.  * @author markliu 
  10.  * 
  11.  */  
  12. public class DynamicInvocationHandler implements InvocationHandler {  
  13.     // 这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象  
  14.     private Object realSubject; // 保存的是真实对象  
  15.   
  16.     public DynamicInvocationHandler() {  
  17.     }  
  18.   
  19.     public DynamicInvocationHandler(Object realSubject) {  
  20.         this.realSubject = realSubject;  
  21.     }  
  22.   
  23.       /* 
  24.        * 这个方法不是我们显示的去调用 
  25.        * 实际内部调用的形式是invoke(this, m3, null); 
  26.        * this指$Proxy0动态代理对象, 
  27.        * m3代表$Proxy0所实现的接口Subject中的request的方法对象 
  28.        */  
  29.     @Override  
  30.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  31.         System.out.println("before calling " + method);  
  32.         method.invoke(realSubject, args);  
  33.         System.out.println("after calling " + method);  
  34.   
  35.         return null;  
  36.     }  
  37.   
  38. }  

       RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

[java]  view plain  copy
  1. package com.markliu.proxy;  
  2.   
  3. /** 
  4.  * 真实角色->被代理类:实现了Subject的request()方法   
  5.  * @author markliu 
  6.  * 
  7.  */  
  8. public class RealSubject implements Subject {  
  9.       
  10.     @Override  
  11.     public void request() {  
  12.         System.out.println("From real subject.");  
  13.     }  
  14. }  
      客户端测试代码:

[java]  view plain  copy
  1. package com.markliu.proxy;  
  2.   
  3. import java.lang.reflect.Field;  
  4. import java.lang.reflect.InvocationHandler;  
  5. import java.lang.reflect.Method;  
  6. import java.lang.reflect.Proxy;  
  7.   
  8. /** 
  9.  * 客户端:生成代理实例,并调用了request()方法   
  10.  * @author markliu 
  11.  */  
  12. public class Client {  
  13.   
  14.     public static void main(String[] args) throws Throwable {  
  15.         // 真实角色->被代理类  
  16.         Subject realSubject = new RealSubject();  
  17.         // 调用处理的业务具体实现  
  18.         InvocationHandler h = new DynamicInvocationHandler(realSubject);  
  19.         // Returns the runtime class of this Object  
  20.         Class<?> cls = realSubject.getClass();  
  21.           
  22.         /* 
  23.          * 动态代理类 
  24.          *  Returns an instance of a proxy class for the specified interfaces  
  25.          *  that dispatches method invocations to the specified invocation handler. 
  26.          *  返回一个指定接口的proxy类对象,用于将方法调用分派到指定的InvocationHandler中 
  27.          *   
  28.          *  cls.getInterfaces(): 获取cls所对应的类RealSubject锁实现的接口,即Subject 
  29.          *  因此创建的Proxy的一个实例实现了动态指定的接口! 
  30.          */  
  31.         Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);  
  32.   
  33.         // 这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口  
  34.         System.out.println(subject instanceof Proxy);  
  35.   
  36.         // 这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口  
  37.         System.out.println("subject的Class类是:" + subject.getClass().toString());  
  38.   
  39.         System.out.print("subject中的属性有:");  
  40.         Field[] field = subject.getClass().getDeclaredFields();  
  41.         for (Field f : field) {  
  42.             System.out.print(f.getName() + ", ");  
  43.         }  
  44.   
  45.         System.out.print("\n" + "subject中的方法有:");  
  46.   
  47.         Method[] method = subject.getClass().getDeclaredMethods();  
  48.         for (Method m : method) {  
  49.             System.out.print(m.getName() + ", ");  
  50.         }  
  51.   
  52.         System.out.println("\n" + "subject的父类是:" + subject.getClass().getSuperclass());  
  53.         System.out.print("subject实现的接口是:");  
  54.   
  55.         Class<?>[] interfaces = subject.getClass().getInterfaces();  
  56.         for (Class<?> i : interfaces) {  
  57.             System.out.print(i.getName() + ", ");  
  58.         }  
  59.   
  60.         System.out.println("\n\n" + "运行结果为:");  
  61.         subject.request();  
  62.     }  
  63. }  

程序运行结果:

[java]  view plain  copy
  1. true  
  2. subject的Class类是:class com.sun.proxy.$Proxy0  
  3. subject中的属性有:m1, m2, m3, m0,   
  4. subject中的方法有:request, equals, toString, hashCode,   
  5. subject的父类是:class java.lang.reflect.Proxy  
  6. subject实现的接口是:com.markliu.proxy.Subject,   
  7.   
  8. 运行结果为:  
  9. before calling public abstract void com.markliu.proxy.Subject.request()  
  10. From real subject.  
  11. after calling public abstract void com.markliu.proxy.Subject.request()  

     这个结果的信息非常重要。要理解subject和Proxy之间的联系,request()和invoke()的调用关系。

    由运行结果可以看出:

  • subject instanceof Proxy为true,subject是Proxy的一个实例,这个实例实现了Subject接口

  • subject的Class类是com.sun.proxy.$Proxy0,是一个通过反射机制动态生成的,下面分析其源代码

  • subject中只有四个方法,其中一个就是抽象主题角色中的方法request

     从Client中的代码看,从newProxyInstance这个方法入手,看一下Proxy类中newProxyInstance方法的源代码,是如何将interfaces和invacationhandler建立联系,创建了动态代理类。


[java]  view plain  copy
  1.   @CallerSensitive  
  2.   public static Object newProxyInstance(ClassLoader loader,  
  3.                                         Class<?>[] interfaces,  
  4.                                         InvocationHandler h)  
  5.       throws IllegalArgumentException  
  6.   {  
  7. ...  
  8.       final Class<?>[] intfs = interfaces.clone();  
  9.   
  10.       /* 
  11.        * 根据类加载器loader获取实现intfs接口的类类型,其实就是com.sun.proxy.$Proxy0 
  12.        */  
  13.       Class<?> cl = getProxyClass0(loader, intfs);  
  14.   
  15.       /* 
  16.        * 通过指定的handler调用com.sun.proxy.$Proxy0的构造方法. 
  17.        */  
  18.       try {  
  19.         ...  
  20.           /* 
  21.            * 获取public类型的构造函数 
  22.            * 其中有定义private static final Class<?>[] constructorParams = { InvocationHandler.class }; 
  23.            * 打开getConstructor(constructorParams)方法: 
  24.            * 会看到底层调用的是: 
  25.            *     private Constructor<T> getConstructor0(Class<?>[] parameterTypes, 
  26.            *                           int which) throws NoSuchMethodException 
  27.     *       { 
  28.     *          // 获取所有的public类型的构造函数 
  29.     *           Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); 
  30.     *           for (Constructor<T> constructor : constructors) { 
  31.     *              // 如果传入的构造函数的参数parameterTypes(也就是客户端传入的实现InvocationHandler接口的对象h) 
  32.     *              // 与其中的public类型的构造函数的参数匹配,就返回该构造函数,即此Demo中的public DynamicProxySubject(Object realSubject); 
  33.     *              // 从而实现了在动态代理类中持有InvocationHandler的引用,则可以调用invoke方法 
  34.     *               if (arrayContentsEq(parameterTypes, constructor.getParameterTypes())) { 
  35.     *                   return getReflectionFactory().copyConstructor(constructor); 
  36.     *               } 
  37.     *           } 
  38.     *           throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes)); 
  39.     *       } 
  40.            */  
  41.           final Constructor<?> cons = cl.getConstructor(constructorParams);  
  42.           final InvocationHandler ih = h;  
  43.           if (!Modifier.isPublic(cl.getModifiers())) {  
  44.               AccessController.doPrivileged(new PrivilegedAction<Void>() {  
  45.                   public Void run() {  
  46.                       cons.setAccessible(true);  
  47.                       return null;  
  48.                   }  
  49.               });  
  50.           }  
  51.           // 此处创建了该实例,传入InvocationHandler对象  
  52.           return cons.newInstance(new Object[]{h});  
  53.       } catch (Exception e) {  
  54.         ...  
  55.       }  
  56.   }  

       Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
        (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0。$Proxy0类 实现了interfaces的接口,并继承了Proxy类.
        (2)实例化$Proxy0并在构造方法中把InvocationHandler对象传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:

[java]  view plain  copy
  1. public class Proxy implements java.io.Serializable {  
  2.   
  3.     protected InvocationHandler h;  
  4.     protected Proxy(InvocationHandler h) {  
  5.         Objects.requireNonNull(h);  
  6.         this.h = h;  
  7.     }  
  8.     ...  
  9. }  
       这样该动态代理类就持有InvocationHandler的引用,则可以调用invoke方法。下面具体看下该动态代理类,继承了Proxy的$Proxy0的源代码:

[java]  view plain  copy
  1. package com.markliu.proxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.lang.reflect.Proxy;  
  6. import java.lang.reflect.UndeclaredThrowableException;  
  7.   
  8. public final class $Proxy0 extends Proxy implements Subject {  
  9.     private static Method m1;  
  10.     private static Method m0;  
  11.     private static Method m3;  
  12.     private static Method m2;  
  13.   
  14.     static {  
  15.         try {  
  16.             m1 = Class.forName("java.lang.Object").getMethod("equals",  
  17.                     new Class[] { Class.forName("java.lang.Object") });  
  18.             m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
  19.                     new Class[0]);  
  20.             m3 = Class.forName("***.RealSubject").getMethod("request",  
  21.                     new Class[0]);  
  22.             m2 = Class.forName("java.lang.Object").getMethod("toString",  
  23.                     new Class[0]);  
  24.   
  25.         } catch (NoSuchMethodException nosuchmethodexception) {  
  26.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
  27.         } catch (ClassNotFoundException classnotfoundexception) {  
  28.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
  29.         }  
  30.     }   
  31.   
  32.     public $Proxy0(InvocationHandler invocationhandler) {  
  33.         super(invocationhandler);  
  34.     }  
  35.   
  36.     @Override  
  37.     public final boolean equals(Object obj) {  
  38.         try {  
  39.             return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
  40.         } catch (Throwable throwable) {  
  41.             throw new UndeclaredThrowableException(throwable);  
  42.         }  
  43.     }  
  44.   
  45.     @Override  
  46.     public final int hashCode() {  
  47.         try {  
  48.             return ((Integer) super.h.invoke(this, m0, null)).intValue();  
  49.         } catch (Throwable throwable) {  
  50.             throw new UndeclaredThrowableException(throwable);  
  51.         }  
  52.     }  
  53.   
  54.     public final void request() {  
  55.         try {  
  56.             // 此处调用了InvocationHandler接口中的invoke方法!  
  57.             super.h.invoke(this, m3, null);  
  58.             return;  
  59.         } catch (Error e) {  
  60.         } catch (Throwable throwable) {  
  61.             throw new UndeclaredThrowableException(throwable);  
  62.         }  
  63.     }  
  64.   
  65.     @Override  
  66.     public final String toString() {  
  67.         try {  
  68.             return (String) super.h.invoke(this, m2, null);  
  69.         } catch (Throwable throwable) {  
  70.             throw new UndeclaredThrowableException(throwable);  
  71.         }  
  72.     }  
  73. }  

     所以可以看出动态代理类$Proxy0实现了Subject接口,在Demo中调用subject.request()时,由里氏代换原则知,将调用$Proxy0中的request的方法,最终调用实现InvocationHandler接口的invoke方法。

   总结:JDK动态代理的类结构如下:

       因为java已经为我们创建了Proxy类,所以需要有办法告诉Proxy类你需要做什么。你不能像以前一样直接把代码放在Proxy类中,因为Proxy类不是你直接实现的。需要将实际添加的业务逻辑放在实现InvocationHandler中。InvocationHandler的工作是响应代理的任何调用,即调用生成的代理类的方法时最终会交给InvocationHandler的invoke方法,其中就会调用RealSubject的方法。你可以把InvocationHandler想成是代理收到方法调用后,请求做实际业务处理的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值