Java進階--Java動態代理
阿新 • • 發佈:2020-10-21
JDK version: 1.8
動態代理中所說的“動態”, 是針對使用Java程式碼實際編寫了代理類的“靜態”代理而言的, 它的優勢不在於省去了編寫代理類那一點編碼工作量,
而是實現了可以在原始類和介面還未知的時候, 就確定代理類的代理行為,當代理類與原始類脫離直接聯絡後, 就可以很靈活地重用於不同的應用場景之中。
- 目前Java開發包中包含了對動態代理的支援, 但是其實現只支援對介面的的實現。其實現主要通過 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 介面。
- Proxy 類主要用來獲取動態代理物件, InvocationHandler 介面用來約束呼叫者實現。
- 動態代理是很多框架和技術的基礎, spring 的 AOP 實現就是基於動態代理實現的。
Proxy類
Proxy 提供用於建立動態代理類例項的靜態方法, 它還是與之建立的所有動態代理類的超類。
介紹一下 Proxy 類中最常用的方法 java.lang.reflect.Proxy#newProxyInstance,
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { ... return cons.newInstance(new Object[]{h}); ... }
該方法返回動態代理類例項. 該方法有三個引數
- loader: the class loader to define the proxy class. 與原始類的類載入器一致
- interfaces: the list of interfaces for the proxy class to implement. 原始類實現的介面
- h: InvocationHandler 例項, 動態代理類例項會呼叫 InvocationHandler 例項的 invoke 方法
InvocationHandler介面
InvocationHandler 介面中只有一個方法.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
該方法有三個引數
- proxy: 動態代理類的例項
- method: 原始類的 Method 例項
- args: 被代理 Method 需要的引數
下面通過編寫一個簡單地動態代理類舉例說明:
1 public class DomainProxyTest { 2 3 interface IGreet { 4 5 void sayHello(); 6 7 void sayHi(); 8 } 9 10 static class Greet implements IGreet { 11 12 @Override 13 public void sayHello() { 14 System.out.println("Hello World!"); 15 } 16 17 @Override 18 public void sayHi() { 19 System.out.println("Hi there!"); 20 } 21 } 22 23 static class DynamicProxy implements java.lang.reflect.InvocationHandler { 24 25 Object originalObj; 26 27 Object bind(Object originalObj) { 28 this.originalObj = originalObj; 29 return java.lang.reflect.Proxy 30 .newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this); 31 } 32 33 @Override 34 public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable { 35 System.out.println("welcome!"); 36 return method.invoke(originalObj, args); 37 } 38 } 39 40 public static void main(String[] args) { 41 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 42 IGreet greet = (IGreet) new DynamicProxy().bind(new Greet()); 43 greet.sayHello(); 44 greet.sayHi(); 45 } 46 }
編譯 Java 檔案:
javac DomainProxyTest.java
執行 class 檔案:
發現生成了一個名稱為 $Proxy0.class 的檔案, 該檔案就是 Java 動態生成的代理類. 反編譯看一下其內容:
import DomainProxyTest.IGreet; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; final class $Proxy0 extends Proxy implements IGreet { private static Method m1; private static Method m3; private static Method m4; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void sayHello() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void sayHi() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("DomainProxyTest$IGreet").getMethod("sayHello"); m4 = Class.forName("DomainProxyTest$IGreet").getMethod("sayHi"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
程式碼邏輯很簡單, 呼應了文章上面對動態代理的描述.
點選連結加入QQ群: 282575808【網際網路技術交流群】:https://jq.qq.com/?_wv=1027&k=Iw86