標籤:
打算用JAVA實現一個簡單的RPC架構,看完RPC參考代碼之後,感覺RPC的實現主要用到了兩個方面的JAVA知識:網路通訊和動態代理。因此,先補補動態代理的知識。---多看看代碼中寫的注釋
參考:Java 代理模式與動態代理類
java的動態代理機制詳解
在動態代理中,首先定義一個介面,這個介面中聲明的方法 是 真實類需要實現的,真實類實現該方法來提供具體的操作。
public interface Subject { public abstract void request();}
public class RealSubject extends Subject{//具體實作類別 public RealSubject(){ } public void request(){ System.out.println("From Real Subject"); }}
有了具體實作類別,現在就需要代理類了,具體實作類別在本例中為RealSubject.java ,它就是 被代理的類,即代理類 代理的“傢伙”就是 具體實作類別。
代理類需要實現 InvocationHandler 介面,為什麼呢?先瞭解下JAVA動態代理中需要用到的一個介面:InvocationHandler 和 一個類: java.lang.reflect.Proxy
InvocationHandler介面中只有一個invoke方法,該方法需要三個參數:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
proxy:需要代理的真實類的對象
method:“待調用的真實對象的某個方法”
args:調用該方法時需要的參數
InvocationHandler的作用就是將需要代理的類的對象作為建構函式的參數傳給它,即傳給代理類,這樣代理類就知道自己代理的對象是什麼了。DynamicSubject implements InvocationHandler
//給構造方法傳遞不同的類型的 被代理的對象,就可以 實現 動態代理--即在運行時確定被代理的對象的類型 public DynamicSubject(Object obj){ sub = obj; }
同時,在用戶端代碼中進行方法調用時,會自動執行InvocationHandler介面的invoke方法,從而由InvocationHandler介面的invoke方法 中的 method.invoke() 再去執行真正的被代理類的方法。
method.invoke(sub, args);//調用 實際 的方法,調用被代理的對象的方法,即RealSubject.request()
看看JDK中java.lang.reflect.Method 中的invoke() 方法的定義:對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。----即調用 指定對象 sub ,指定參數 args 的方法。
再看看Proxy類的作用,Proxy類主要用來在Clinet代碼中產生一個動態對象。該對象調用方法來開始進行動態代理。
/* 第一個參數指定哪個 ClassLoader對象來載入我們的代理對象 * 第二個參數 為代理對象提供的介面是真實對象所實現的介面,表示我要代理的是該真實對象,這樣我就能調用這組介面中的方法了 * 第三個參數handler, 我們這裡將這個代理對象關聯到了上方的 InvocationHandler 這個對象上,這樣,當執行下條實際方法調用語句時,就可以
* 知道委託的是哪個InvocationHandler 了,進行就會自動執行該 InvocationHandler 的 invoke方法 */Subject subject = (Subject)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler); /* * 當該語句執行時,它會委託到InvocationHandler 類中的 invoke方法,並執行 method.invoke()進行實際調用 * 注意:InvocationHandler.invoke()中有兩條輸出語句,運行Client後在控制台中看到了其輸出結果,說明該方法被委託執行了 */ subject.request();
完整的動態代理類的實現代碼如下:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class DynamicSubject implements InvocationHandler{ private Object sub;//被 代理的對象,它是一個Object類型的對象,說明,被 代理的對象是可以動態改變的 public DynamicSubject(){ } //給構造方法傳遞不同的類型的 被代理的對象,就可以 實現 動態代理--即在運行時確定被代理的對象的類型 public DynamicSubject(Object obj){ sub = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ System.out.println("before calling " + method); /* * 在構造方法中獲得了被代理的對象RealSubject sub */ method.invoke(sub, args);//調用 實際 的方法,調用被代理的對象的方法,即RealSubject.request() System.out.println("after calling " + method); return null; }}
完整的用戶端實現代碼如下:Client.java中的 subject.request(); 表示開始執行代理調用。可以從代碼中看出,subject 對象是由 Proxy 動態產生的。
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class Client { public static void main(String[] args) throws Exception{ RealSubject rs = new RealSubject();// 需要 被 代理的類,即用戶端現在需要的代理一個類型為 RealSubject 的對象 /*
* 注意,代理類的構造方法的參數為Object類型,說明它可以代理 任意類型的對象---即程式運行前DynamicSubject並不知道需要代理的對象
* AnOtherRealSubject anotherRs = new AnOtherRealSubject();
* InvocationHandler handler = new DynamicSubject(anotherRs);
*
*/ InvocationHandler handler = new DynamicSubject(rs);// 代理類,將需要 被 代理的類 作為 代理類的構造 函數的參數傳入 Class cls = rs.getClass(); /* * Class c = Proxy.getProxyClass(cls.getClassLoader(), cls.getInterfaces()); Constructor ct = c.getConstructor(new Class[]{InvocationHandler.class}); Subject subject = (Subject) ct.newInstance(new Object[]{handler}); */ /* * 第二個參數 cls.getInterfaces()... cls 是代表RealSubject類型的 Class對象,參考JDK中Class類的getInterfaces()
*表明:newProxyInstance 知道動態產生的代理對象subject 需要實現哪些介面---需要實現的介面由getInterfaces()指定。
*而cls 是一個代表RealSubject類型的Class對象,RealSubject 實現了 Subject 介面,因此動態產生的subject 對象當然可以
*強制類型轉換了。 */ Subject subject = (Subject)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler); /* * 當該語句執行時,它會委託到InvocationHandler 類中的 invoke方法,並執行 method.invoke()進行實際調用 * 注意:InvocationHandler.invoke()中有兩條輸出語句,運行Client後在控制台中看到了其輸出結果,說明該方法被委託執行了 */ subject.request(); /* * 輸出:com.sun.proxy.$Proxy0 * 這表明,subject 物件類型是 $Proxy0,而不是 Subject 或 InvocationHandler 類型 * 但是在 22行語句中,卻可以將之進行強制類型轉換,轉成Subject類型 * 原因是:Proxy.newProxyInstance產生的是一個動態對象,即在JVM運行時產生的。在newProxyInstance()的第二個參數上,給它提供了一組介面 * 該代理對象就會實現這組介面,因此也就可以將該對象強制轉化為這組介面中的任意一個 */ System.out.println(subject.getClass().getName()); }}
JAVA 動態代理學習記錄