JDK动态代理

1、基本概念

1.什么是代理?

在阐述JDK动态代理以前,咱们颇有必要先来弄明白代理的概念。代理这个词自己并非计算机专用术语,它是生活中一个经常使用的概念。这里引用维基百科上的一句话对代理进行定义:java

A proxy is an agent or substitute authorized to act for another person or a document which authorizes the agent so to act.程序员

意思是说:代理指的是一个代理人(或替代品),它被受权表明另一我的(或文档)。api

从这个简明扼要的定义中,能够看出代理的一些特性:1.代理存在的意义就是表明另外一个事物。2.代理至少须要完成(或实现)它所表明的事物的功能。数组

 

2.什么是JAVA静态代理?

JAVA静态代理是指由程序员建立或工具生成的代理类,这个类在编译期就已是肯定了的,存在的。缓存

典型的静态代理模式通常包含三类角色:安全

1.抽象角色:它的做用是定义一组行为规范。抽象角色通常呈现为接口(或抽象类),这些接口(或抽象类)中定义的方法就是待实现的。数据结构

2.真实角色:实现了抽象角色所定义的行为。真实角色就是个普通的类,它须要实现抽象角色定义的那些接口。app

3.代理角色:表明真实角色的角色。根据上面代理的定义,咱们能够知道代理角色须要至少完成(或实现)真实角色的功能。为了完成这一使命,那么代理角色也须要实现抽象角色所定义的行为(即代理类须要实现抽象角色所定义的接口),而且在实现接口方法的时候须要调用真实角色的相应方法。  上图使用UML类图解释了静态代理的数据模型。ide

1.接口IFunc表明了抽象角色,定义了一个行为,即方法doSomething()。函数

2.类RealFunc表明了真实角色,它实现了IFunc接口中定义的方法doSomething()。

3.类ProxyFunc表明了代理角色,它实现了IFunc接口中定义的方法doSomething()。它的实现方式是依赖RealFunc类的,经过持有RealFunc类对象的引用realObj,在ProxyFunc.doSomething()方法中调用了realObj.doSomething()。固然,代理类也能够作一些其余的事情,如图中的doOtherthing()。

经过上面的介绍,能够看出静态代理存在如下问题:

1.代理类依赖于真实类,由于代理类最根本的业务功能是须要经过调用真实类来实现的。那么若是事先不知道真实类,该如何使用代理模式呢?

2.一个真实类必须对应一个代理类,即当有多个真实类RealA、RealB、RealC...的时候,就须要多个代理类ProxyA、ProxyB、ProxyC...。这样的话若是大量使用静态代理,容易致使类的急剧膨胀。该如何解决?

要想解决上述问题,就须要使用下面讲解的JAVA动态代理。

3.什么是JAVA动态代理?

JAVA动态代理与静态代理相对,静态代理是在编译期就已经肯定代理类和真实类的关系,而且生成代理类的。而动态代理是在运行期利用JVM的反射机制生成代理类,这里是直接生成类的字节码,而后经过类加载器载入JAVA虚拟机执行。如今主流的JAVA动态代理技术的实现有两种:一种是JDK自带的,就是咱们所说的JDK动态代理,另外一种是开源社区的一个开源项目CGLIB。本文主要对JDK动态代理作讨论。

4.什么是JDK动态代理?

JDK动态代理的实现是在运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具类去生成一个代理类和代理类实例。

JDK动态代理的类关系模型和静态代理看起来差很少。也是须要一个或一组接口来定义行为规范。须要一个代理类来实现接口。区别是没有真实类,由于动态代理就是要解决在不知道真实类的状况下依然可以使用代理模式的问题。 图中高亮显示的$Proxy0即为JDK动态代理技术生成的代理类,类名的生成规则是前缀"$Proxy"加上一个序列数。这个类继承Proxy,实现一系列的接口Intf1,Intf2...IntfN。

既然要实现接口,那么就要实现接口的各个方法,即图中的doSomething1(),doSomething2()...doSomethingN()。咱们上面介绍静态代理的时候,知道静态代理类本质上是调用真实类去实现接口定义的方法的。可是JDK动态代理中是没有真实类这样的概念的。那么JDK动态代理类是如何实现这些接口方法的具体逻辑呢?答案就在图中的InvocationHandler上。$Proxy0对外只提供一个构造函数,这个构造函数接受一个InvocationHandler实例h,这个构造函数的逻辑很是简单,就是调用父类的构造函数,将参数h赋值给对象字段h。最终就是把全部的方法实现都分派到InvocationHandler实例h的invoke方法上。因此JDK动态代理的接口方法实现逻辑是彻底由InvocationHandler实例的invoke方法决定的。

2、样例分析

了解了JDK动态代理的概念后,如今咱们动手写个JDK动态代理的代码样例。直观的认识下JDK动态代理技术为咱们的作了什么。上面说到了,JDK动态代理主要依靠Proxy和InvocationHandler这两个类来生成动态代理类和类的实例。这两个类都在jdk的反射包java.lang.reflect下面。Proxy是个工具类,有了它就能够为接口生成动态代理类了。若是须要进一步生成代理类实例,须要注入InvocationHandler实例。这点咱们上面解释过,由于代理类最终逻辑的实现是分派给InvocationHandler实例的invoke方法的。

闲话休絮,先开始咱们的第一步,定义一个接口。这个接口里面定义一个方法helloWorld()。

1 public interface MyIntf {
2     void helloWorld();
3 }

第二步,编写一个咱们本身的调用处理类,这个类须要实现InvocationHandler接口

1 public class MyInvocationHandler implements InvocationHandler {
2     @Override
3     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
4         System.out.println(method);
5         return null;
6     }
7 }

InvocationHandler接口只有一个待实现的invoke方法。这个方法有三个参数,proxy表示动态代理类实例,method表示调用的方法,args表示调用方法的参数。在实际应用中,invoke方法就是咱们实现业务逻辑的入口。这里咱们的实现逻辑就一行代码,打印当前调用的方法(在实际应用中这么作是没有意义的,不过这里咱们只想解释JDK动态代理的原理,因此越简单越清晰)。

第三步,直接使用Proxy提供的方法建立一个动态代理类实例。并调用代理类实例的helloWorld方法,检测运行结果。

1 public class ProxyTest {
2     public static void main(String[] args) {
3         System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
4         MyIntf proxyObj = (MyIntf)Proxy.newProxyInstance(MyIntf.class.getClassLoader(),new Class[]{MyIntf.class},new MyInvocationHandler());
5         proxyObj.helloWorld();
6     }
7 }

第三行代码是设置系统属性,把生成的代理类写入到文件。这里再强调一下,JDK动态代理技术是在运行时直接生成类的字节码,并载入到虚拟机执行的。这里不存在class文件的,因此咱们经过设置系统属性,把生成的字节码保存到文件,用于后面进一步分析。

第四行代码就是调用Proxy.newProxyInstance方法建立一个动态代理类实例,这个方法须要传入三个参数,第一个参数是类加载器,用于加载这个代理类。第二个参数是Class数组,里面存放的是待实现的接口信息。第三个参数是InvocationHandler实例。

第五行调用代理类的helloWorld方法,运行结果:

public abstract void com.tuniu.distribute.openapi.common.annotation.MyIntf.helloWorld()

分析运行结果,就能够发现,方法的最终调用是分派到了MyInvocationHandler.invoke方法,打印出了调用的方法信息。

到这里,对于JDK动态代理的基本使用就算讲完了。咱们作的事情不多,只是编写了接口MyIntf和调用处理类MyInvocationHandler。其余大部分的工做都是Proxy工具类帮咱们完成的。Proxy帮咱们建立了动态代理类和代理类实例。上面的代码咱们设置了系统属性,把生成的字节码保存到class文件。下面咱们经过反编译软件(如jd-gui),看下Proxy类为咱们生成的代理类是什么样子的。

 1 package com.sun.proxy;
 2 import com.tuniu.distribute.openapi.common.annotation.MyIntf;
 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 MyIntf {
 9     private static Method m0;
10     private static Method m1;
11     private static Method m2;
12     private static Method m3;
13 
14     static {
15         try {
16             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
17             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
18             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
19             m3 = Class.forName("com.tuniu.distribute.openapi.common.annotation.MyIntf").getMethod("helloWorld", new Class[0]);
20             return;
21         } catch (NoSuchMethodException localNoSuchMethodException) {
22             throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
23         } catch (ClassNotFoundException localClassNotFoundException) {
24             throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
25         }
26     }
27 
28     public $Proxy0(InvocationHandler paramInvocationHandler) {
29         super(paramInvocationHandler);
30     }
31 
32     public final void helloWorld() {
33         try {
34             this.h.invoke(this, m3, null);
35             return;
36         } catch (Error | RuntimeException localError) {
37             throw localError;
38         } catch (Throwable localThrowable) {
39             throw new UndeclaredThrowableException(localThrowable);
40         }
41     }
42 
43     // 后面省略equals(),hashCode(),toString()三个方法的代码,由于这三个方法和helloWorld()方法很是类似
44 }

这里Proxy为咱们生成的代理类叫$Proxy0,继承了Proxy,实现了咱们定义的接口MyIntf。每个JDK动态代理技术生成的代理类的名称都是由$Proxy前缀加上一个序列数0,1,2...。而且都须要继承Proxy类。

$Proxy0类中9-26行代码定义了4个Method字段m0,m1,m2,m3,咱们先来看下m3,它描述了咱们定义的接口MyIntf中的方法helloWorld。

紧接着下面的32-41行代码就是对helloWorld方法的实现,它的实现很是简单就一句话this.h.invoke(this, m3, null);这行代码就是调用当前对象的h实例的invoke方法,也就是把方法的实现逻辑分派给了h.invoke。这里的h是继承父类Proxy中的InvocationHandler字段(读者能够结合上面的动态代理类图模型或者Proxy源码进一步理解)。

同时$Proxy0提供了一个构造函数(代码28-30行),调用父类的构造函数来注入这个InvocationHandler实例。

$Proxy0中的另外3个Method对象m0,m1,m2分别表明了Object类的hashCode(),equals(),toString()方法,咱们知道java中的全部类都是Object的子类(Object类自己除外),这里$Proxy0重写了Object中的这三个方法。这三个方法的实现和helloWorld方法很相似,因此笔者这里就把这段代码省略了,用一行注释(43行代码)解释了下。

行文至此,咱们已经感官的认识了运行时生成的代理类结构。揭开了这层面纱,其实JDK动态代理也没什么了。简单的来讲就是,JDK动态代理技术能够为一组接口生成代理类,这个代理类也就是一层壳,简单的实现了接口中定义的方法。经过提供一个构造函数传入InvocationHandler实例,而后将方法的具体实现交给它。

3、源码分析

前两部分分别从概念和应用的角度阐述了JDK动态代理技术。最后一部分咱们将从Proxy源码对JDK动态代理进行深刻的剖析。Proxy类对外提供了4个静态方法,分别为:

1 public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces);
2 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
3 public static boolean isProxyClass(Class<?> cl);
4 public static InvocationHandler getInvocationHandler(Object proxy);

下面咱们经过各个方法的源码依次分析。

1.getProxyClass

getProxyClass方法返回代理类的Class实例。这个代理类就是类加载器loader定义的、实现了一些列接口interfaces的。若是以前已经为这个loader和interfaces建立过代理类,那么直接返回这个代理类的Class实例。若是没有,则动态建立并返回。

1 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException {
2     final Class<?>[] intfs = interfaces.clone();
3     final SecurityManager sm = System.getSecurityManager();
4     if (sm != null) {
5         checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
6     }
7     return getProxyClass0(loader, intfs);
8 }

getProxyClass方法并无JDK动态代理的核心逻辑:第二行将接口的Class数组interfaces进行克隆。3-6行是类加载器和接口访问权限的校验(这里虚拟机的安全性相关逻辑,不是咱们JDK代理技术的关注点,因此不作过多解释)。关键的逻辑就最后一行代码,调用getProxyClass0方法去获取代理类的Class实例。

1 private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
2     if (interfaces.length > 65535) {
3         throw new IllegalArgumentException("interface limit exceeded");
4     }
5     return proxyClassCache.get(loader, interfaces);
6 }

getProxyClass0也没有包含JDK动态代理的核心逻辑:2-4行只是对接口的个数进行了简单的校验,不能超过65535,咱们在实际应用中通常也不会出现这种状况。最后一行代码是去缓存对象proxyClassCache中获取代理类的Class实例。proxyClassCache是Proxy类的静态变量

private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

proxyClassCache是类java.lang.reflect.WeakCache的实例,经过类名就能够看出来这个类是用来作缓存的。Proxy调用WeakCache提供的构造函数,传入KeyFactory实例和ProxyClassFactory实例(这两个实例的用途后面会讲到)。在分析WeakCache的get方法源码以前,咱们先来大概介绍下WeakCache缓存的数据结构。

 

WeakCache缓存的数据结构是(key,sub-key)->(value)。这个结构和Redis里面的hash结构很相似,根据一级的键(key)、二级的键(sub-key)为索引,去检索出值(value)。对应到WeakCache类代码里面,就是一个ConcurrentMap实例map,这个map的key就是一级键,map的value又是个ConcurrentMap实例,这个子map的key是二级键,子map的value就是缓存的的值。上面图中的箭头就表示着对应关系,一目了然。图中下半部分是JDK动态代理缓存的键值生成规则,后面会一一详解。下面咱们看下WeakCache的get方法源码。

 1 public V get(K key, P parameter) {
 2     Objects.requireNonNull(parameter);
 3     expungeStaleEntries();
 4     Object cacheKey = CacheKey.valueOf(key, refQueue);
 5     ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
 6     if (valuesMap == null) {
 7         ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>());
 8         if (oldValuesMap != null) {
 9             valuesMap = oldValuesMap;
10         }
11     }
12     Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
13     Supplier<V> supplier = valuesMap.get(subKey);
14     Factory factory = null;
15     while (true) {
16         if (supplier != null) {
17             V value = supplier.get();
18             if (value != null) {
19                 return value;
20             }
21         }
22         if (factory == null) {
23             factory = new Factory(key, parameter, subKey, valuesMap);
24         }
25         if (supplier == null) {
26             supplier = valuesMap.putIfAbsent(subKey, factory);
27             if (supplier == null) {
28                 supplier = factory;
29             }
30         } else {
31             if (valuesMap.replace(subKey, supplier, factory)) {
32                 supplier = factory;
33             } else {
34                 supplier = valuesMap.get(subKey);
35             }
36         }
37     }
38 }

咱们的调用语句是proxyClassCache.get(loader, interfaces),也就是说形参key是类加载器loader,parameter是接口的Class数组interfaces。

第2行代码是对形参parameter(interfaces)进行非空的校验,若是为空则抛出空指针异常。第3行代码是去除缓存中的陈旧数据,这里不是咱们的关注点,就不详细介绍了。

第4行是根据形参key(loader)计算出缓存的一级键cacheKey,这里咱们不去看具体的生成逻辑,只须要大概知道一级键是根据形参key(loader)算出来的,这里能够用一个数学函数表达式描述这个关系:key=f(loader)

第5行代码是根据一级键查出值,这个值的Map实例valuesMap。因为以前没有为这个loader和interfaces建立过代理类,因此valuesMap为null,6-11行代码会被执行,这几行代码就是给valueMap一个初始值,而后结合上面算出来的一级键cacheKey塞进缓存实例map里面。

第12行根据key(loader)和parameter(interfaces)计算出缓存的二级键subKey。这里的subKeyFactory是Proxy调用WeakCache提供的构造函数时,传入的KeyFactory实例(上面提到过)。KeyFactory是Proxy的内部类,咱们简单看下KeyFactory的apply方法,看下是怎么生成二级键的。

1 public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
2     switch (interfaces.length) {
3         case 1: return new Key1(interfaces[0]);
4         case 2: return new Key2(interfaces[0], interfaces[1]);
5         case 0: return key0;
6         default: return new KeyX(interfaces);
7     }
8 }

这里笔者不打算展开分析每行代码,咱们经过上面的代码,只须要大概知道二级键是根据interfaces计算出来的(classLoader这个参数根本没用到)。这里能够用一个数学函数表达式描述这个关系:sub-key=g(interfaces)

咱们继续上面的WeakCache.get方法分析,第13行代码是根据二级键subKey从valuesMap获取值supplier,这个值supplier也就是咱们缓存数据的值。因为valuesMap是新建的,因此supplier为null。

15-37行是个循环,第一次进入的时候,factory和supplier都为null。因此22-30行代码将被执行。第23行代码是调用Factory构造函数建立一个实例factory(FactoryWeakCache的内部类),这个构造函数就是简单把传入的参数赋值给factory实例的字段。接下来26-29将构造的二级键subKeyfactory塞进valuesMap,并将factory赋给supplierFactory类继承Supplier类)到这里缓存数据的初始化就算告一段落了,一级键是根据loader计算出来的cacheKey,二级键是根据interfaces计算出来的subKey,值是new的一个factory(Supplier实例)

第一遍的循环并无建立代理类,只是作了一些初始化的工做,下面继续执行这个循环体(15-37行)。此次supplier不为null了(就是上面的Factory实例factory),因此进入16-21行的代码块,第17行实质就是调用factory.get方法。这个方法返回的value也就是动态代理类的Class实例。紧接着第19行就把这个value返回。下面来看下Factory的get方法的源码。

 1 public synchronized V get() {
 2     Supplier<V> supplier = valuesMap.get(subKey);
 3     if (supplier != this) {
 4         return null;
 5     }
 6     V value = null;
 7     try {
 8         value = Objects.requireNonNull(valueFactory.apply(key, parameter));
 9     } finally {
10         if (value == null) {
11             valuesMap.remove(subKey, this);
12         }
13     }
14     assert value != null;
15     CacheValue<V> cacheValue = new CacheValue<>(value);
16     if (valuesMap.replace(subKey, this, cacheValue)) {
17         reverseMap.put(cacheValue, Boolean.TRUE);
18     } else {
19         throw new AssertionError("Should not reach here");
20     }
21     return value;
22 }

第2行代码是根据二级键subKey获得值supplier,也就是咱们在上面的WeakCache的get方法中建立的Factory实例factory

接下来的几行代码没什么好讲的,直接看第8行代码,这行代码调用了valueFactory.apply方法建立动态代理类并将结果赋值给变量value。

9-14行针对建立代理类失败的状况下作的处理和判断逻辑。若是建立代理类成功,则继续执行后面的代码。

第15行代码把生成的代理类的Class实例(即value变量)进行缓存。缓存的值并非直接的value,而是由value构造的一个CacheValue实例cacheValue,因为CacheValue实现了接口Value,而Value接口继承了Supplier接口,因此cacheValue就是Supplier的实例。这里咱们不须要去深究CacheValue的数据结构,只须要知道缓存的值是根据代理类的Class实例去计算的,这里能够用一个数学函数表达式描述这个关系:value=h($ProxyX.class)

第16行代码将二级键subKey和cacheValue放入valuesMap(valuesMap的类型是ConcurrentMap<Object, Supplier<V>>)。第18行是记录缓存状态的。方法的最后将代理类的Class实例value返回。

这个方法的主要逻辑是对缓存的操做,动态代理类的建立动做是经过调用valueFactory.apply获得的。这里的valueFactory是在构造WeakCache时传入的参数,上面提到的ProxyClassFactory实例。因为FactoryWeakCache的内部类,因此在Factory的get方法中可使用这个实例valueFactory。下面咱们就来看下ProxyClassFactoryapply方法。

 1 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 2     Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
 3     for (Class<?> intf : interfaces) {
 4         Class<?> interfaceClass = null;
 5         try {
 6             interfaceClass = Class.forName(intf.getName(), false, loader);
 7         } catch (ClassNotFoundException e) {}
 8         if (interfaceClass != intf) {
 9             throw new IllegalArgumentException(intf + " is not visible from class loader");
10         }
11         if (!interfaceClass.isInterface()) {
12             throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");
13         }
14         if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
15             throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
16         }
17     }
18     String proxyPkg = null;
19     int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
20     for (Class<?> intf : interfaces) {
21         int flags = intf.getModifiers();
22         if (!Modifier.isPublic(flags)) {
23             accessFlags = Modifier.FINAL;
24             String name = intf.getName();
25             int n = name.lastIndexOf('.');
26             String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
27             if (proxyPkg == null) {
28                 proxyPkg = pkg;
29             } else if (!pkg.equals(proxyPkg)) {
30                 throw new IllegalArgumentException("non-public interfaces from different packages");
31             }
32         }
33     }
34     if (proxyPkg == null) {
35         proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
36     }
37     long num = nextUniqueNumber.getAndIncrement();
38     String proxyName = proxyPkg + proxyClassNamePrefix + num;
39     byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
40     try {
41         return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
42     } catch (ClassFormatError e) {
43         throw new IllegalArgumentException(e.toString());
44     }
45 }

第2-17行代码,是几个简单的校验工做:1.接口interfaces是否对类加载器loader可见(即接口interfaces是由类加载器loader加载的或者由loader的上一级类加载器加载的,这点须要读者了解JVM的类加载知识),2.参数interfaces是否都是接口,3.参数interfaces里面有没有重复数据。

第18-26行代码,是为即将生成的代理类计算出访问标志和包名。具体的约束和规则以下:

1.若是全部的接口的访问标志都是public,那么生成的代理类的访问标志是final public,不然是final。

2.对于访问标志不是public的接口,它们必需要在同一个包下,不然抛出异常。

3.若是存在访问标志不是public的接口,那么生成的代理类的包名就是这些接口的包名。不然包名是默认的ReflectUtil.PROXY_PACKAGE(即com.sun.proxy)。

第37-38行代码,是计算代理类的权限定名。代理类的简单名称生成规则前面介绍过,是特定前缀"$Proxy"加上一个序列数(0,1,2,3...)。

第39行调用ProxyGenerator的静态方法generateProxyClass去建立代理类的字节码。这个方法咱们就不跟进去看了,由于它的逻辑就是根据一些固化的规则(好比代理类里面要实现接口的方法,实现Object的equals、hashCode、toString方法,提供一个形参为InvocationHandler实例的构造函数等等),依据JAVA虚拟机规范中定义的Class类文件结构去生成字节码的。咱们以前在第二部分“样例分析”中经过反编译软件,观察分析过生成的代理类结构。这里读者能够和前面的内容融会贯通一下。

第41行代码,调用Proxy的本地方法defineClass0将生成的代理类字节码加载到虚拟机,并返回代理类的Class实例。(若是读者对类加载的只是感兴趣的话,能够去深刻学习下JAVA虚拟机的类加载机制)

到目前为止,JDK动态代理类的建立流程就所有结束了,咱们说的是首次建立代理类的状况,如今咱们回头来看下WeakCache的get方法,若是以前已经为类加载器loader和接口interfaces建立过了代理类,那么调用这个方法的时候是个什么样子呢?答案就是根据一级键和二级键直接从缓存中取到代理类的Class实例。这里就再也不逐行分析代码了,读者本身理解下。最后用一张简单概要的时序图描绘一下Proxy的getProxyClass0方法生成代理类的调用流程。

 

2.newProxyInstance

有了上面对动态代理类的建立过程的系统理解,如今来看newProxyInstance方法就容易多了,它就是使用反射机制调用动态代理类的构造函数生成一个代理类实例的过程。

 1 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException {
 2     Objects.requireNonNull(h);
 3     final Class<?>[] intfs = interfaces.clone();
 4     final SecurityManager sm = System.getSecurityManager();
 5     if (sm != null) {
 6         checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
 7     }
 8     Class<?> cl = getProxyClass0(loader, intfs);
 9     try {
10         if (sm != null) {
11             checkNewProxyPermission(Reflection.getCallerClass(), cl);
12         }
13         final Constructor<?> cons = cl.getConstructor(constructorParams);
14         final InvocationHandler ih = h;
15         if (!Modifier.isPublic(cl.getModifiers())) {
16             AccessController.doPrivileged(new PrivilegedAction<Void>() {
17                 public Void run() {
18                     cons.setAccessible(true);
19                     return null;
20                 }
21             });
22         }
23         return cons.newInstance(new Object[]{h});
24     } catch (IllegalAccessException | InstantiationException e) {
25         throw new InternalError(e.toString(), e);
26     } catch (InvocationTargetException e) {
27         Throwable t = e.getCause();
28         if (t instanceof RuntimeException) {
29             throw (RuntimeException) t;
30         } else {
31             throw new InternalError(t.toString(), t);
32         }
33     } catch (NoSuchMethodException e) {
34         throw new InternalError(e.toString(), e);
35     }
36 }

第2-7行代码,克隆interfaces,并进行权限相关校验,和前面getProxyClass方法相似,这里不作赘述。

第8行getProxyClass0方法获取代理类的Class实例,这个方法在上面也详细介绍过了。

第10-12行也是进行权限校验,检查调用者是否对这个代理类有访问权限。

第13-23行就是构造代理类实例的过程。先获取代理类的构造函数,接着对其访问权限进行判断,若是不是public,则将其设置可访问的。最后利用反射机制调用构造方法,传入参数InvocationHandler的实例h,建立代理类实例并返回。

3.isProxyClass

这个方法用于判断一个类是否是代理类。

1 public static boolean isProxyClass(Class<?> cl) {
2     return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
3 }

Proxy.class.isAssignableFrom(cl)这句话简而言之就是判断cl所表示的类是否是Proxy或者Proxy的子类。由于全部的代理类都集成Proxy,因此这个条件必须知足。知足这个条件也不能保证就是代理类,由于可能存在人为地编写一个类继承Proxy这种状况。proxyClassCache.containsValue(cl)这个方法是检查缓存中是否存在这个Class实例cl。我前面分析过,但凡生成的代理类都会被缓存,因此这个方法才是检测一个类是不是代理类的惟一标准。

4.getInvocationHandler

这个方法用于获取代理类中的InvocationHandler实例。这个方法没有什么太多的逻辑,基本就是判断下传入的对象是不是代理类,以及一些访问权限的校验。当这些都合法的状况下,返回InvocationHandler实例。

 1 public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException {
 2     if (!isProxyClass(proxy.getClass())) {
 3         throw new IllegalArgumentException("not a proxy instance");
 4     }
 5     final Proxy p = (Proxy) proxy;
 6     final InvocationHandler ih = p.h;
 7     if (System.getSecurityManager() != null) {
 8         Class<?> ihClass = ih.getClass();
 9         Class<?> caller = Reflection.getCallerClass();
10         if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),ihClass.getClassLoader())) {
11             ReflectUtil.checkPackageAccess(ihClass);
12         }
13     }
14     return ih;
15 }

至此,关于JDK动态代理的技术都所有讲解完了。本文从基本概念、样例分析、源码分析三个角度去分析说明JDK动态代理背后的知识,但愿对读者有所帮助。

做者:南唐三少
出处:http://www.cnblogs.com/nantang
若是您以为阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是咱们最大的写做动力!欢迎各位转载,可是未经做者本人赞成,转载文章以后必须在文章页面明显位置给出做者和原文连接,不然保留追究法律责任的权利。

相关文章
相关标签/搜索