深入理解JDK動態代理機制
基於JDK8
Java中代理的實現一般分為三種
JDK靜態代理
JDK動態代理
CGLIB動態代理
在Spring的AOP實現中,主要應用了JDK動態代理以及CGLIB動態代理。本文討論關於JDK動態代理機制。
代理一般實現的模式為JDK靜態代理:建立一個介面,然後建立被代理的類實現該介面並且實現該介面中的抽象方法。之後再建立一個代理類,同時使其也實現這個介面。在代理類中持有一個被代理物件的引用,而後在代理類方法中呼叫該物件的方法。
其實就是代理類為被代類預處理訊息、過濾訊息並在此之後將訊息轉發給被代類,之後還能進行訊息的後置處理。代理類和被代類通常會存在關聯關係(即上面提到的持有的被代物件的引用),代理類本身不實現服務,而是透過呼叫被代類中的方法來提供服務。
介面
被代類
代理類
測試
我們可以看出,使用JDK靜態代理很容易就完成了對一個類的代理操作。但是JDK靜態代理的缺點也暴露了出來:由於代理只能為一個類服務,如果需要代理的類很多,那麼就需要編寫大量的代理類,比較繁瑣。
JDK動態代理其實也是基本介面實現的。因為透過介面指向實現類例項的多型方式,可以有效地將具體實現與呼叫解耦,便於後期的修改和維護。
JDK靜態代理與JDK動態代理之間有些相似,比如說都要建立代理類,以及代理類都要實現介面等。
但是不同之處也明顯,
靜態代理 我們需要對某個介面和某個被代類建立代理類,所以我們在編譯前就需要代理類實現與被代類相同的介面,並且直接在實現的方法中呼叫被代類相應的方法
動態代理 則不同,我們不知道要針對哪個介面、哪個被代理類建立代類,因為它是在執行時被建立的
一句話總結JDK靜態代理和動態代理區別,然後開始探究JDK動態代理的底層實現機制:
靜態代理:透過直接編碼建立的
動態代理:利用反射機制在執行時建立代理類
其實在動態代理中,核心是InvocationHandler。每一個代理的例項都會有一個關聯的呼叫處理程式(InvocationHandler)。對待代理例項進行呼叫時,將對方法的呼叫進行編碼並指派到它的呼叫處理器(InvocationHandler)的invoke方法。所以對代理物件例項方法的呼叫都是透過InvocationHandler中的invoke方法來完成的,而invoke方法會根據傳入的代理物件、方法名稱以及引數決定呼叫代理的哪個方法。
我們從JDK動態代理的測試類中可以發現代理類生成是透過Proxy類中的newProxyInstance來完成的,下面我們進入這個方法看一看
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //如果h為空將丟擲異常 Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone();//複製被代理類實現的一些介面,用於後面許可權方面的一些檢查 final SecurityManager sm = System.getSecurityManager(); if (sm != null) { //在這裡對某些安全許可權進行檢查,確保我們有許可權對預期的被代理類進行代理 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 下面這個方法將產生代理類 */ Class<?> cl = getProxyClass0(loader, intfs); /* * 使用指定的呼叫處理程式獲取代理類的建構函式物件 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; //假如代理類的建構函式是private的,就使用反射來set accessible if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } //根據代理類的建構函式來生成代理類的物件並返回 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
所以代理類其實是透過getProxyClass方法來生成的
/** * 生成一個代理類,但是在呼叫本方法之前必須進行許可權檢查 */ private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //如果介面數量大於65535,丟擲非法引數錯誤 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 如果在快取中有對應的代理類,那麼直接返回 // 否則代理類將有 ProxyClassFactory 來建立 return proxyClassCache.get(loader, interfaces); }
那麼ProxyClassFactory是什麼呢?
/** * 裡面有一個根據給定ClassLoader和Interface來建立代理類的工廠函式 * */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 代理類的名字的字首統一為“$Proxy” private static final String proxyClassNamePrefix = "$Proxy"; // 每個代理類字首後面都會跟著一個唯一的編號,如$Proxy0、$Proxy1、$Proxy2 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * 驗證類載入器載入介面得到物件是否與由apply函式引數傳入的物件相同 */ Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * 驗證這個Class物件是不是介面 */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * 驗證這個介面是否重複 */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // 宣告代理類所在的package int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * 記錄一個非公共代理介面的包,以便在同一個包中定義代理類。同時驗證所有非公共 * 代理介面都在同一個包中 */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.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) { // 如果全是公共代理介面,那麼生成的代理類就在com.sun.proxy package下 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * 為代理類生成一個name package name + 字首+唯一編號 * 如 com.sun.proxy.$Proxy0.class */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 生成指定代理類的位元組碼檔案 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return 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()); } } }
由上方程式碼byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);可以看到,其實生成代理類位元組碼檔案的工作是透過 ProxyGenerate類中的generateProxyClass方法來完成的。
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); // 真正用來生成代理類位元組碼檔案的方法在這裡 final byte[] var4 = var3.generateClassFile(); // 儲存代理類的位元組碼檔案 if(saveGeneratedFiles) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { try { int var1 = var0.lastIndexOf(46); Path var2; if(var1 > 0) { Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]); Files.createDirectories(var3, new FileAttribute[0]); var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { var2 = Paths.get(var0 + ".class", new String[0]); } Files.write(var2, var4, new OpenOption[0]); return null; } catch (IOException var4x) { throw new InternalError("I/O exception saving generated file: " + var4x); } } }); } return var4; }
下面來看看真正用於生成代理類位元組碼檔案的generateClassFile方法:
private byte[] generateClassFile() { //下面一系列的addProxyMethod方法是將介面中的方法和Object中的方法新增到代理方法中(proxyMethod) this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; //獲得介面中所有方法並新增到代理方法中 for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; Method[] var5 = var4.getMethods(); int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11 = this.proxyMethods.values().iterator(); //驗證具有相同方法簽名的方法的返回型別是否一致 List var12; while(var11.hasNext()) { var12 = (List)var11.next(); checkReturnTypes(var12); } //後面一系列的步驟用於寫代理類Class檔案 Iterator var15; try { //生成代理類的建構函式 this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); //將代理類欄位宣告為Method,並且欄位修飾符為 private static. //因為 10 是 ACC_PRIVATE和ACC_STATIC的與運算 故代理類的欄位都是 private static Method *** this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); //生成代理類的方法 this.methods.add(var16.generateMethod()); } } //為代理類生成靜態程式碼塊對某些欄位進行初始化 this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } if(this.methods.size() > 'uffff') { //代理類中的方法數量超過65535就拋異常 throw new IllegalArgumentException("method limit exceeded"); } else if(this.fields.size() > 'uffff') {// 代理類中欄位數量超過65535也拋異常 throw new IllegalArgumentException("field limit exceeded"); } else { // 後面是對檔案進行處理的過程 this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); var1 = this.interfaces; var2 = var1.length; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; this.cp.getClass(dotToSlash(var4.getName())); } this.cp.setReadOnly(); ByteArrayOutputStream var13 = new ByteArrayOutputStream(); DataOutputStream var14 = new DataOutputStream(var13); try { var14.writeInt(-889275714); var14.writeShort(0); var14.writeShort(49); this.cp.write(var14); var14.writeShort(this.accessFlags); var14.writeShort(this.cp.getClass(dotToSlash(this.className))); var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); var14.writeShort(this.interfaces.length); Class[] var17 = this.interfaces; int var18 = var17.length; for(int var19 = 0; var19 < var18; ++var19) { Class var22 = var17[var19]; var14.writeShort(this.cp.getClass(dotToSlash(var22.getName()))); } var14.writeShort(this.fields.size()); var15 = this.fields.iterator(); while(var15.hasNext()) { ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); var20.write(var14); } var14.writeShort(this.methods.size()); var15 = this.methods.iterator(); while(var15.hasNext()) { ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); var21.write(var14); } var14.writeShort(0); return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } }
下面是將介面與Object中一些方法新增到代理類中的addProxyMethod方法:
private void addProxyMethod(Method var1, Class<?> var2) { String var3 = var1.getName();//獲得方法名稱 Class[] var4 = var1.getParameterTypes();//獲得方法引數型別 Class var5 = var1.getReturnType();//獲得方法返回型別 Class[] var6 = var1.getExceptionTypes();//異常型別 String var7 = var3 + getParameterDescriptors(var4);//獲得方法簽名 Object var8 = (List)this.proxyMethods.get(var7);//根據方法前面獲得proxyMethod的value if(var8 != null) {//處理多個代理介面中方法重複的情況 Iterator var9 = ((List)var8).iterator(); while(var9.hasNext()) { ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next(); if(var5 == var10.returnType) { ArrayList var11 = new ArrayList(); collectCompatibleTypes(var6, var10.exceptionTypes, var11); collectCompatibleTypes(var10.exceptionTypes, var6, var11); var10.exceptionTypes = new Class[var11.size()]; var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes); return; } } } else { var8 = new ArrayList(3); this.proxyMethods.put(var7, var8); } ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null)); }
作者:芥末無疆sss
連結:
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2331/viewspace-2816426/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入理解靜態代理與JDK動態代理JDK
- java基礎:深入理解JDK動態代理JavaJDK
- 深入理解動態代理
- JDK動態代理JDK
- 深入理解 Java 反射和動態代理Java反射
- Java代理(jdk靜態代理、動態代理和cglib動態代理)JavaJDKCGLib
- JDK動態代理初探JDK
- JDK動態代理和CGLib代理JDKCGLib
- JDK動態代理和 CGLIB 代理JDKCGLib
- 代理模式詳解:靜態代理、JDK動態代理與Cglib動態代理模式JDKCGLib
- 11.JDK動態代理理解與攔截器JDK
- Java動態代理和反射機制Java反射
- spring aop原理 JDK動態代理和CGLIB動態代理SpringJDKCGLib
- 深挖JDK動態代理(一)JDK
- JDK動態代理詳解JDK
- 靜態代理和動態代理(jdk/cglib)詳解JDKCGLib
- Java動態代理(JDK和cglib)JavaJDKCGLib
- Spring AOP --JDK動態代理方式SpringJDK
- Java代理機制分析——JDK代理(Proxy、InvocationHandler與示例)JavaJDK
- 你必須會的 JDK 動態代理和 CGLIB 動態代理JDKCGLib
- 基於 CGLIB 庫的動態代理機制CGLib
- 靜態代理、動態代理與Mybatis的理解MyBatis
- 輕鬆理解 Java 靜態代理/動態代理Java
- 【乾貨】JDK動態代理的實現原理以及如何手寫一個JDK動態代理JDK
- cglib動態代理和jdk動態代理的區別與應用CGLibJDK
- Java架構-Java JDK 動態代理Java架構JDK
- 【深度思考】聊聊JDK動態代理原理JDK
- 深挖JDK動態代理(二):JDK動態生成後的位元組碼分析JDK
- 深入理解DOM事件機制事件
- Android 深入理解 Notification 機制Android
- 深入理解 Kafka 副本機制Kafka
- 一文學會 Java 動態代理機制Java
- Java進階 | Proxy動態代理機制詳解Java
- Spring動態代理的生成-如何判斷是使用JDK動態代理還是CGlib代理SpringJDKCGLib
- 看過Jdk動態代理類長啥樣嗎?Jdk動態代理原理原始碼一本到JDK原始碼
- 有點深度的聊聊JDK動態代理JDK
- 純手寫實現JDK動態代理JDK
- 基於JDK的動態代理原理分析JDK