Java中动态代理实现原理深究

Posted 小杜比亚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中动态代理实现原理深究相关的知识,希望对你有一定的参考价值。

一、前言

  笔者平时开发使用“动态代理”不多,最近在看设计模式的时候,“动态代理”又在面前晃了几次,所以这次想从源码的角度去分析动态代理的实现原理,以窥探其精妙~

二、正文

2.1 静态代理

   本文源码基于jdk1.6.0_33

   在正式剖析动态代理的源码之前,我们可以先来看看“静态代理”(就是我们普通的代理模式)的UML图:

  从上图可以看出,代理类“ProxySubject”和被代理类“RealSubject”实现相同接口,并且“ProxySubject”关联“RealSubject”,客户端调用的时候,实际是调用“ProxySubject”,然后“ProxySubject”调用“RealSubject”同名方法,方法的最终实现是由“RealSubject”提供,“ProxySubject”可以实现对“RealSubject”的访问控制,也可以对对应的方法进行“增强”。下面举个简单的例子说明一下:

// Subject 
public interface Subject {
    public void doSomething();
}

//ProxySubject 
public class ProxySubject implements Subject {
    private Subject realSubject = new RealSubject();
    @Override
    public void doSomething() {
        System.out.println("before doSomething~");
        realSubject.doSomething();
        System.out.println("after doSomething~");
    }

}

//RealSubject 
public class RealSubject implements Subject {

    @Override
    public void doSomething() {
        System.out.println("小杜比亚在写博客");
    }

}

//Client
public class Client {

    public static void main(String[] args) {
        Subject subject = new ProxySubject();
        subject.doSomething();
    }

}

//输出
before doSomething~
小杜比亚在写博客
after doSomething~

  那么动态代理和这个静态代理有什么区别?这就体现在一个“动”字,从上面的代码我们知道,静态代理的“代理类”是程序员写好的,而动态代理的“代理类”是在程序运行期间动态生成的~有些刚开始接触Java编程的读者可能会很好奇,Java的类可以动态生成?当然可以,我之前有篇关于“类”加载的博文:深入探讨Java类加载机制里面有写到,程序运行期间,类加载器会将java字节码文件加载进内存,这边的“加载”是从磁盘上加载,java程序只要字节码文件,至于你的来源(网络、或者按照字节码文件格式进行动态生成),人家是不管的。

  直接切入正题,“动态代理”中牵扯到两个类:java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy,我们先看下一个简单的动态代理例子:

 1 //Subject 
 2 package com.proxy.main;
 3 public interface Subject {
 4     public void doSomething();
 5 }
 6 
 7 //RealSubject 
 8 package com.proxy.main;
 9 public class RealSubject implements Subject {
10 
11     @Override
12     public void doSomething() {
13         System.out.println("小杜比亚还在写博客,有点儿想玩游戏了,坚持~");
14     }
15 }
16 
17 //ProxyHandler
18 package com.proxy.main;
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.Method;
21 
22 public class ProxyHandler implements InvocationHandler {
23     private Object proxiedObj;
24     public ProxyHandler(Object proxiedObj){
25         this.proxiedObj = proxiedObj;
26     }
27     /**
28      * @author caoyg
29      * @date 2017-05-06
30      * @description设置“被代理”对象
31      * @param proxiedObj
32      */
33     public void setProxiedObject(Object proxiedObj){
34         this.proxiedObj = proxiedObj;
35     }
36     @Override
37     public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
38         return method.invoke(proxiedObj, args);
39     }
40 
41 }
42 
43 //Client
44 package com.proxy.main;
45 import java.lang.reflect.Proxy;
46 
47 public class Client {
48 
49     public static void main(String[] args) {
50         Subject sb = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
51                                                       new Class[]{Subject.class} , 
52                                                       new ProxyHandler(new RealSubject()));
53         sb.doSomething();
54     }
55 
56 }
57 
58 //输出
59 小杜比亚还在写博客,有点儿想玩游戏了,坚持~

  上面代码第37行,invoke方法三个参数含义如下:

1 /**
2  该方法负责集中处理动态代理类上的所有方法调用。
3  第一个参数是动态生成的代理类实例,
4  第二个参数是被调用的方法对象
5  第三个方法是方法调用参数。
6  调用处理器根据这三个参数进行预处理或分派到委托类实例上执行
7 */
8 public Object invoke(Object proxy, Method method, Object[] args)
9     throws Throwable;

 

2.2 动态代理机制

  1、先创建一个InvocationHandler实现类,并且将需要“被代理”的对象传递给它(类似下面代码

  

2、Proxy通过方法newProxyInstance接收classLoader和一组interfaces(“被代理”对象实现的接口)以及InvocationHandler的实现类实例(以下简称“h”),通过classLoader和interfaces构造动态代理类的Class(下面代码是Proxy的源码部分

 1 Class cl = getProxyClass(loader, interfaces);
 2 ............
 3 public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)throws IllegalArgumentException
 4     {
 5         //......省略
 6     byte[] proxyClassFile =    ProxyGenerator.generateProxyClass(proxyName, interfaces);
 7     try {
 8         proxyClass = defineClass0(loader, proxyName,
 9         proxyClassFile, 0, proxyClassFile.length);
10     } catch (ClassFormatError e) {
11         /*
12         * A ClassFormatError here means that (barring bugs in the
13         * proxy class generation code) there was some other
14         * invalid aspect of the arguments supplied to the proxy
15         * class creation (such as virtual machine limitations
16         * exceeded).
17         */
18         throw new IllegalArgumentException(e.toString());
19     }
20     //......省略
21 return proxyClass; 22 }

  3、Proxy通过反射机制,将传入的h作为参数,调用动态代理对应的构造函数,返回最终的动态代理实例

 

  在调用动态代理类的方法时,动态代理将方法的调用分派转发给InvocationHandler的invoke方法,InvocationHandler的invoke方法又将方法调用分派给它本身引用的“被代理”类对应的方法。

 

2.3 动态代理注意点

  1、包:代理接口都是public修饰的,则代理类被定义在顶层包(package为空),如果访问修饰符是默认(default),那么代理类被定义在该接口所在包下面

 1     /*
 2            * Record the package of a non-public proxy interface so that the
 3            * proxy class will be defined in the same package.  Verify that
 4            * all non-public proxy interfaces are in the same package.
 5            */
 6           for (int i = 0; i < interfaces.length; i++) {
 7               int flags = interfaces[i].getModifiers();
 8               if (!Modifier.isPublic(flags)) {
 9                  String name = interfaces[i].getName();
10                  int n = name.lastIndexOf(\'.\');
11                  String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
12                  if (proxyPkg == null) {
13                    proxyPkg = pkg;
14                  } else if (!pkg.equals(proxyPkg)) {
15                    throw new IllegalArgumentException("non-public interfaces from different packages");
16                  }
17              }
18          }
19  
20          if (proxyPkg == null) {    // if no non-public proxy interfaces,
21            proxyPkg = "";        // use the unnamed package
22          }

  2、生成的代理类为public final,不能被继承

3、类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率

1      /*
2          * Choose a name for the proxy class to generate.代理类类名生成规则
3          */
4         long num;
5         synchronized (nextUniqueNumberLock) {
6             num = nextUniqueNumber++;
7         }
8         String proxyName = proxyPkg + proxyClassNamePrefix + num;
 1 synchronized (cache) {
 2     /*
 3     * Note that we need not worry about reaping the cache for
 4     * entries with cleared weak references because if a proxy class
 5     * has been garbage collected, its class loader will have been
 6     * garbage collected as well, so the entire cache will be reaped
 7     * from the loaderToCache map.
 8     */
 9     do {
10         Object value = cache.get(key);
11         if (value instanceof Reference) {
12             proxyClass = (Class) ((Reference) value).get();
13         }
14         if (proxyClass != null) {
15             // proxy class already generated: return it
16             return proxyClass;
17         } else if (value == pendingGenerationMarker) {
18             // proxy class being generated: wait for it
19             try {
20                 cache.wait();
21             } catch (InterruptedException e) {
22                 /*
23                 * The class generation that we are waiting for should
24                 * take a small, bounded time, so we can safely ignore
25                 * thread interrupts here.
26                 */
27             }
28             continue;
29         } else {
30             /*
31             * No proxy class for this list of interfaces has been
32             * generated or is being generated, so we will go and
33             * generate it now. Mark it as pending generation.
34             */
35             cache.put(key, pendingGenerationMarker);
36             break;
37         }
38     } while (true);
39 }

  4、类继承关系

  

  Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理,cglib解决了这个问题)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。

  5、代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,代码在反编译中

1 //ProxyGenerator.class
2  private byte[] generateClassFile()
3   {
4     addProxyMethod(hashCodeMethod, Object.class);
5     addProxyMethod(equalsMethod, Object.class);
6     addProxyMethod(toStringMethod, Object.class);
7     .....................
8 }

 

2.4 Proxy源码分析

  主要关注一些静态变量和两个方法,我们先来看看Proxy中的静态变量:

 1     /** prefix for all proxy class names */
 2    //类名前缀
 3     private final static String proxyClassNamePrefix = "$Proxy";
 4 
 5     /** parameter types of a proxy class constructor */
 6    //proxy类构造函数的参数类型,这个在通过反射生成动态代理类的时候用到
 7     private final static Class[] constructorParams = { InvocationHandler.class };
 8 
 9     /** maps a class loader to the proxy class cache for that loader */
10     private static Map loaderToCache = new WeakHashMap();
11 
12     /** marks that a particular proxy class is currently being generated */
13     private static Object pendingGenerationMarker = new Object();
14 
15     /** next number to use for generation of unique proxy class names */
16     //下个proxy的序号
17     private static long nextUniqueNumber = 0;
18     private static Object nextUniqueNumberLock = new Object();
19 
20     /** set of all generated proxy classes, for isProxyClass implementation */
21     private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
22 
23     /**
24      * the invocation handler for this proxy instance.
25      * @serial
26      */
27     protected InvocationHandler h;

  接下来是两个重要方法:getProxyClass和newProxyInstance,这个newProxyInstance就是我们在前面的动态代理实例里面直接使用的方法,这个方法调用getProxyClass获取动态代理类的Class对象,然后通过反射生成动态代理类实例并返回:

 1  public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
 2     {
 3     if (h == null) {
 4         throw new NullPointerException();
 5     }
 6 
 7     /*
 8      * Look up or generate the designated proxy class.
 9      */
10     //生成动态代理Class
11     Class cl = getProxyClass(loader, interfaces);
12 
13     /*
14      * Invoke its constructor with the designated invocation handler.
15      */
16     try {
17         Constructor cons = cl.getConstructor(constructorParams);
18         //通过反射生成动态代理类实例对象,并返回
19         return (Object) cons.newInstance(new Object[] { h });
20     } catch (NoSuchMethodException e) {
21         throw new InternalError(e.toString());
22     } catch (IllegalAccessException e) {
23         throw new InternalError(e.toString());
24     } catch (InstantiationException e) {
25         throw new InternalError(e.toString());
26     } catch (InvocationTargetException e) {
27         throw new InternalError(e.toString());
28     }
29     }

  在Proxy整个源码中,主要工作都集中在方法getProxyClass中:

  1 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)throws IllegalArgumentException
  2     {
  3     //接口数的限制
  4     if (interfaces.length > 65535) {
  5         throw new IllegalArgumentException("interface limit exceeded");
  6     }
  7 
  8     Class proxyClass = null;
  9 
 10     /* collect interface names to use as key for proxy class cache */
 11     String[] interfaceNames = new String[interfaces.length];
 12 
 13     Set interfaceSet = new HashSet();    // for detecting duplicates
 14     /*下面这个for循环做三项安全验证:
 15      *1、传入的所有接口是否对loader可见,如果不可见,抛出异常
 16      *2、传入的所有”接口“是否真的都是interface,如果不是,抛出异常
 17      *3、传入的所有接口是否有重复,如果有,抛出异常
 18      */
 19     for (int i = 0; i < interfaces.length; i++) {
 20         /*
 21          * Verify that the class loader resolves the name of this
 22          * interface to the same Class object.
 23          */
 24         String interfaceName = interfaces[i].getName();
 25         Class interfaceClass = null;
 26         try {
 27         interfaceClass = Class.forName(interfaceName, false, loader);
 28         } catch (ClassNotFoundException e) {
 29         }
 30         if (interfaceClass != interfaces[i]) {
 31         throw new IllegalArgumentException(
 32             interfaces[i] + " is not visible from class loader");
 33         }
 34 
 35         /*
 36          * Verify that the Class object actually represents an
 37          * interface.
 38          */
 39         if (!interfaceClass.isInterface()) {
 40         throw new IllegalArgumentException(
 41             interfaceClass.getName() + " is not an interface");
 42         }
 43 
 44         /*
 45          * Verify that this interface is not a duplicate.
 46          */
 47         if (interfaceSet.contains(interfaceClass)) {
 48         throw new IllegalArgumentException(
 49             "repeated interface: " + interfaceClass.getName());
 50         }
 51         interfaceSet.add(interfaceClass);
 52 
 53         interfaceNames[i] = interfaceName;
 54     }
 55 
 56     /*
 57      * Using string representations of the proxy interfaces as
 58      * keys in the proxy class cache (instead of their Class
 59      * objects) is sufficient because we require the proxy
 60      * interfaces to be resolvable by name through the supplied
 61      * class loader, and it has the advantage that using a string
 62      * representation of a class makes for an implicit weak
 63      * reference to the class.
 64      */
 65     //接口名称列表(List类型)
 66     Object key = Arrays.asList(interfaceNames);
 67 
 68     /*
 69      * Find or create the proxy class cache for the class loader.(查询或者创建代理类的缓存,缓存未找到或者已经失效,那么重新创建一个)
 70      */
 71     Map cache;
 72     synchronized (loaderToCache) {
 73         cache = (Map) loaderToCache.get(loader);
 74         if (cache == null) {
 75         cache = new HashMap();
 76         loaderToCache.put(loader, cache);
 77         }
 78         /*
 79          * This mapping will remain valid for the duration of this
 80          * method, without further synchronization, because the mapping
 81          * will only be removed if the class loader becomes unreachable.
 82          */
 83     }
 84 
 85     /*
 86      * Look up the list of interfaces in the proxy class cache using
 87      * the key.  This lookup will result in one of three possible
 88      * kinds of values:
 89      *     null, if there is currently no proxy class for the list of
 90      *         interfaces in the class loader,
 91      *     the pendingGenerationMarker object, if a proxy class for the
 92      *         list of interfaces is currently being generated,
 93      *     or a weak reference to a Class object, if a proxy class for
 94      *         the list of interfaces has already been generated.
 95      */
 96     synchronized (cache) {
 97         /*
 98          * Note that we need not worry about reaping the cache for
 99          * entries with cleared weak references because if a proxy class
100          * has been garbage collected, its class loader will have been
101          * garbage collected as well, so the entire cache will be reaped
102          * from the loaderToCache map.
103          */
104         do {
105         //接口名称列表作为key,查找代理类缓存中是否已经存在创建好的动态代理类
106         Object value = cache.get(key);
107         if (value instanceof Reference) {
108             proxyClass = (Class) ((Reference) value).get();
109         }
110         //代理类如果已经创建,直接返回
111         if (proxyClass != null) {
112             // proxy class already generated: return it
113             return proxyClass;
114         } else if (value == pendingGenerationMarker) {//动态代理类还处于创建状态,那么当前线程进入等待
115             // proxy class being generated: wait for it
116             try {
117             cache.wait();
118             } catch (InterruptedException e) {
119             /*
120              * The class generation that we are waiting for should
121              * take a small, bounded time, so we can safely ignore
122              * thread interrupts here.
123              */
124             }
125             continue;//被唤醒之后,进行第二次检查
126         } else以上是关于Java中动态代理实现原理深究的主要内容,如果未能解决你的问题,请参考以下文章

JAVA 动态代理原理和实现

java动态代理:JDK动态代理的内部原理

动态代理—— CGLIB代理原理

代理的实现原理是啥?

java如何实现拦截短信功能

JDK动态代理实现原理