===========================================html
原文連接: JDK動態代理實現機制 轉載請註明出處!java
===========================================數據庫
本文只對JDK動態代理的底層實現進行分析,如需瞭解代理模式和動態代理的使用請移步:設計模式—代理模式 動態代理的兩種實現方式(JDK/Cglib)設計模式
在讀代碼以前先清楚兩個概念:api
一、Class類存儲的是類的全部信息,包括類的全部方法、屬性、實現接口等。每一個類對應一個Class對象(單例),Class對象是由classLoader加載出來的,使用雙親委派模型來保證class只會被加載一次。緩存
二、classLoader在加載類的時候無論class文件是從哪裏來的,不管是從.class文件、網絡、數據庫類加載器都不關心。他只關心給他的class二進制流是否是可以經過校驗。安全
說明:如下測試代碼和 動態代理的兩種實現方式(JDK/Cglib)相同網絡
使用JDK動態代理須要實現InvocationHandler接口,同時實現invoke()方法。app
package com.zpj.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Created by Perkins on 2017/4/2. */ public class JDKProxy implements InvocationHandler { private Object person; public Object getInstance(Object person) { this.person = person; return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("doSomething---------start"); method.invoke(person, args); System.out.println("doSomething---------end"); return null; } }
測試代碼以下:ide
package com.zpj.proxy.jdk; /** * Created by Perkins on 2017/4/2. */ public class Run { public static void main(String[] args) { Person person = (Person) new JDKProxy().getInstance(new MrLi()); person.doWork(); } }
運行的時候在person處打斷點可看到person的類名爲$Proxy0而不是Person或者MrLi,則說明該返回對象是Person的實現類。
咱們添加以下代碼把person的class中的方法打印出來
package com.zpj.proxy.jdk; /** * Created by Perkins on 2017/4/2. */ import java.lang.reflect.Method; public class Run { private Method method; public static void main(String[] args) { Person person = (Person) new JDKProxy().getInstance(new MrLi()); Method [] methods = person.getClass().getMethods(); for(int i =0; i<methods.length;i++){ System.out.println(methods[i].getName()); } person.doWork(); } }
結果以下,很明顯紅框中的方法不屬於Person也不屬於Object中的方法。這更進一步說明返回的person並非Person的實例。
下面就進入底層代碼對JDK動態代理進行解析。
這裏直接對Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);進行分析。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{ if (h == null) { //驗證InvocationHandler不容許爲null throw new NullPointerException(); } /* * Look up or generate the designated proxy class. */ //調用getProxyClass()獲取Class實例,該實例即是返回的代理Person的實例,此方法爲重點!!! Class cl = getProxyClass(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try { //利用反射機制從Class中取出構造器建立對象 Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
在上面方法中調用public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)throws IllegalArgumentException獲取了Class實例,下面進入該方法進行分析。
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { if (interfaces.length > 65535) { //驗證接口數量不容許超過65535 throw new IllegalArgumentException("interface limit exceeded"); } //************開始對interface進行循環驗證,驗證經過則加入interfaceNames中*************************** Class proxyClass = null; /* collect interface names to use as key for proxy class cache */ String[] interfaceNames = new String[interfaces.length]; Set interfaceSet = new HashSet(); // for detecting duplicates for (int i = 0; i < interfaces.length; i++) {//循環對全部接口進行操做 /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { //根據名稱獲取接口的Class interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException(interfaces[i] + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } //************結束對interface進行循環驗證,存儲於interfaceNames中*************************** /* * Using string representations of the proxy interfaces as * keys in the proxy class cache (instead of their Class * objects) is sufficient because we require the proxy * interfaces to be resolvable by name through the supplied * class loader, and it has the advantage that using a string * representation of a class makes for an implicit weak * reference to the class. */ Object key = Arrays.asList(interfaceNames); /* * Find or create the proxy class cache for the class loader. */ Map cache; synchronized (loaderToCache) { cache = (Map) loaderToCache.get(loader); if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } /* * This mapping will remain valid for the duration of this * method, without further synchronization, because the mapping * will only be removed if the class loader becomes unreachable. */ } /* * Look up the list of interfaces in the proxy class cache using * the key. This lookup will result in one of three possible * kinds of values: * null, if there is currently no proxy class for the list of * interfaces in the class loader, * the pendingGenerationMarker object, if a proxy class for the * list of interfaces is currently being generated, * or a weak reference to a Class object, if a proxy class for * the list of interfaces has already been generated. */ synchronized (cache) { /* * Note that we need not worry about reaping the cache for * entries with cleared weak references because if a proxy class * has been garbage collected, its class loader will have been * garbage collected as well, so the entire cache will be reaped * from the loaderToCache map. */ do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // proxy class already generated: return it return proxyClass; } else if (value == pendingGenerationMarker) { // proxy class being generated: wait for it try { cache.wait(); } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { /* * No proxy class for this list of interfaces has been * generated or is being generated, so we will go and * generate it now. Mark it as pending generation. */ cache.put(key, pendingGenerationMarker); break; } } while (true); } try { String proxyPkg = null; // package to define proxy class in /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ //尋找到package的包路徑,爲構建代理類作準備。同時要保證全部的非public代理接口在相同的包中 for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, proxyPkg = ""; // use the unnamed package } { /* * Choose a name for the proxy class to generate. */ long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } //這裏構建了Class的全類名:包名+$Proxy+num,這裏的proxyClassNamePrefix=「$Proxy」 String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Verify that the class loader hasn't already * defined a class with the chosen name. */ /* * Generate the specified proxy class. */ //該方法爲核心方法,獲取代理類的字節碼數據流。也便是proxyName.class文件的數據流 //由於interface的全部class文件都不已經被加載,因此這裏只須要根據名稱就能夠從JVM中讀取出全部的二進制數據 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); try { //調用native方法從class字節碼文件中建立代理類的Class實例。這裏再也不進入分析,由JVM負責實現 //獲取到代理類的Class實例後,程序就能夠根據代理類Class經過反射進行對象建立 proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); } finally { /* * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated, store it in the cache (with a weak reference); * otherwise, remove the reserved entry. In all cases, notify * all waiters on reserved entries in this cache. */ synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } //返回代理類的Class實例 return proxyClass; } }
在上面的方法中,核心就在於如何構建代理類的class字節碼文件。由於該字節碼文件是由代理類和目標類組合而成,這也便是在運行期間動態建立class的方法。
即:byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);該方法的具體實現,看下面代碼分析
public static byte[] generateProxyClass(final String proxyName, Class[] interfaces) { //建立代理生成器 ProxyGenerator proxyGenerator = new ProxyGenerator(proxyName, interfaces); //調用generateClassFile()生成class的二進制數據流 final byte[] classFile = proxyGenerator.generateClassFile(); //該參數能夠在運行時配置,當爲true時則程序會生成代理類的class文件保存在磁盤中,即: $Proxy0.class if(saveGeneratedFiles) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(proxyName) + ".class"); var1.write(classFile); var1.close(); return null; } catch (IOException var2) { throw new InternalError("I/O exception saving generated file: " + interfaces); } } }); } return classFile; }
繼續進入proxyGenerator.generateClassFile()分析
private byte[] generateClassFile() { //注意這三個方法,hashCodeMethod、equalsMethod、toStringMethod //這三個方法來自於Object,代理類一樣須要Object的這三個方法 //把這三個方法名稱和與之匹配的Object,class進行緩存 this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); int var1; int var3; //這裏循環對目標類所實現的全部接口中的方法進行緩存 for(var1 = 0; var1 < this.interfaces.length; ++var1) { //獲取接口中的方法 Method[] var2 = this.interfaces[var1].getMethods(); for(var3 = 0; var3 < var2.length; ++var3) { this.addProxyMethod(var2[var3], this.interfaces[var1]); } } Iterator var7 = this.proxyMethods.values().iterator(); List var8; //把代理類須要建立的方法緩存在var8中。這裏說明一下,由於該源碼是從class中讀取出來的,因此變量名在進行編譯的時候被更改了,這裏閱讀的時候須要注意一些 while(var7.hasNext()) { var8 = (List)var7.next(); checkReturnTypes(var8); } Iterator var11; try { //添加構造器方法,至此代理類中所須要添加的方法添加完成 this.methods.add(this.generateConstructor()); var7 = this.proxyMethods.values().iterator(); //循環把須要的變量和方法添加在fileds和methods中緩存 while(var7.hasNext()) { var8 = (List)var7.next(); var11 = var8.iterator(); while(var11.hasNext()) { ProxyGenerator.ProxyMethod var4 = (ProxyGenerator.ProxyMethod)var11.next(); this.fields.add(new ProxyGenerator.FieldInfo(var4.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var4.generateMethod()); } } this.methods.add(this.generateStaticInitializer()); } catch (IOException var6) { throw new InternalError("unexpected I/O Exception"); } //對方法和參數進行安全性驗證 if(this.methods.size() > '\uffff') { throw new IllegalArgumentException("method limit exceeded"); } else if(this.fields.size() > '\uffff') { throw new IllegalArgumentException("field limit exceeded"); } else { this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); for(var1 = 0; var1 < this.interfaces.length; ++var1) { this.cp.getClass(dotToSlash(this.interfaces[var1].getName())); } this.cp.setReadOnly(); //構建緩衝流存放動態生成的字節碼文件數據 ByteArrayOutputStream var9 = new ByteArrayOutputStream(); DataOutputStream var10 = new DataOutputStream(var9); try { //這裏就是按照class文件格式進行封裝,這裏再也不詳解 var10.writeInt(-889275714); var10.writeShort(0); var10.writeShort(49); this.cp.write(var10); var10.writeShort(49); var10.writeShort(this.cp.getClass(dotToSlash(this.className))); var10.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); var10.writeShort(this.interfaces.length); for(var3 = 0; var3 < this.interfaces.length; ++var3) { //添加全部方法的字節碼數據 var10.writeShort(this.cp.getClass(dotToSlash(this.interfaces[var3].getName()))); } var10.writeShort(this.fields.size()); var11 = this.fields.iterator(); while(var11.hasNext()) { //添加全部變量的字節碼數據 ProxyGenerator.FieldInfo var12 = (ProxyGenerator.FieldInfo)var11.next(); var12.write(var10); } var10.writeShort(this.methods.size()); var11 = this.methods.iterator(); while(var11.hasNext()) { ProxyGenerator.MethodInfo var13 = (ProxyGenerator.MethodInfo)var11.next(); var13.write(var10); } var10.writeShort(0); //動態組合class二進制字節碼結束,進行返回。這裏存儲的就是一個完整class文件數據。調用處據今生成Class對象實例 return var9.toByteArray(); } catch (IOException var5) { throw new InternalError("unexpected I/O Exception"); } } }
至此,JDK動態代理的實現結束。由以上可見,其核心就是動態的生成代理類的class字節碼數據,而後調用native方法從字節碼數據中建立代理對象的Class實例,拿到Class實例後經過Java的反射技術生成代理類。
那麼該代理類的結果到底如何?咱們能夠在main方法中添加 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");配置,這樣在程序運行的時候會把代理類的字節碼文件保存在類路徑下。經過反編譯能夠讀取到代理類的具體詳情。
下面看反編譯後的代理類文件
package com.sun.proxy; import com.zpj.proxy.jdk.Person; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; //注意這裏$Proxy0 extends Proxy implements Person,這裏也便是目標類必須實現的有接口的緣由 /** * 代理類名稱:$Proxy0 * 繼承:Proxy * 實現:Person */ public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } //繼承自Object的方法 public final boolean equals(Object paramObject) throws { try { return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } //繼承自Object的方法 public final String toString()throws { try { return (String) this.h.invoke(this, m2, null); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } //繼承自Object的方法 public final int hashCode()throws { try { return ((Integer) this.h.invoke(this, m0, null)).intValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } //實現Person的方法 public final void doWork()throws { try { //由此處能夠看出目標方法的調用循序 /** * 代理類先調用實現接口的方法,在該方法中調用InvocationHandler的invoke方法。 * 而在invoke中由經過注入進去的methods經過反射調用目標類的目標方法doWork() */ this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.zpj.proxy.jdk.Person").getMethod("doWork", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
------------end