1. 程式人生 > >java中的動態代理和反射

java中的動態代理和反射

java的動態代理是用反射實現的。

什麼是反射?
java的反射機制,是說在執行時刻,對於任何一個類,都能夠知道它的所有屬性和方法;對任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫方法的功能稱為java的反射機制。

java通過java.lang.Class類實現反射機制。

Class類的getDeclaredMethod可以根據方法名字返回一個方法:

// 返回一個 Method 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)

Method的invoke方法可以執行這個方法:

// 對帶有指定引數的指定物件呼叫由此 Method 物件表示的底層方法。
Object invoke(Object obj, Object... args) 
package com.bike;

import java.lang.reflect.*;

public class Main {
    public static void main(String[] args) throws Exception{
        //返回A的構造方法
        Constructor c = A.class.getConstructor();
        //返回A類的所有為public 宣告的構造方法
Constructor[] cons = A.class.getConstructors(); //返回A類所有的構造方法,包括private Constructor[] cons2 = A.class.getDeclaredConstructors(); //返回A類的第一個public 方法 Method m = A.class.getMethod("say"); //執行 m.invoke(A.class.newInstance(), null); //返回A類所有的public 方法
Method[] ms = A.class.getMethods(); //返回A類所有的方法,包括private Method[] allMs = A.class.getDeclaredMethods(); //返回A類的public欄位 Field field = A.class.getField("i"); System.out.println(field.get(A.class.newInstance())); //返回A類的static 欄位 System.out.println(field.get(null)); } } class A{ public int i = 1; public static int b = 2; public A(){ System.out.println("無參構造"); } private A(String s){ System.out.println("有參構造"+s); } public void say(){ System.out.println("say"); } }

上面例子是反射,但這個例子沒有用,這樣用反射沒有意義。反射怎麼用呢?

上面例子中的類A,在本地,用的是本地類載入器(有待細究),如果A不在本地,而在另一個機器上,通過網路類載入器,載入到本地虛擬機器。那就感覺有點用了,實現在伺服器,在客戶端通過方法名稱和引數,呼叫方法實現,這就是遠端方法呼叫。
這裡寫圖片描述
(參考:java api java.lang.ClassLoader

什麼是靜態代理呢?
這裡寫圖片描述
很簡單,代理類和實現類實現同一個介面,代理類有一個實現類的引用,客戶呼叫代理類的方法時,代理類就呼叫實現類的方法。稱為靜態,是因為代理類和實現類是寫死的,就是在編譯階段就確定的。

// 介面
interface A {
    public void f();
}

// 實現類
class AImpl implements A {
    public void f() {
        System.out.println("A.f()");
    }
}

// 代理類
class AProxy implements A {
    A a;
    public AProxy(A a) {
        this.a = a;
    }
    public void f() {
        // do something else
        a.f();
        // do something else
    }
}

// Client
public static void main() {
    A aImpl = new AImpl();
    AProxy aProxy = new AProxy(aImpl);
    aProxy.f();
}

這個例子好像也沒什麼用,不過可以在代理類的f()方法呼叫實現類的方法前後加上一些程式碼;在構造方法中也可以通過網路類載入器載入別的機器上的類,這樣本地呼叫時,感覺不到遠端機器,就像呼叫原生代碼一樣。

如果有好多類要代理,就要寫好幾個代理類,這樣比較麻煩,這時動態代理就有用了,動態代理的本質就是使用者提供類名、方法名、引數,代理類執行方法,返回結果
用類載入器可以將類載入到虛擬機器,用Class clazz表示,有這個物件,就可以執行它的方法。(這就是反射)
這就實現了動態代理。

具體實現:
這裡寫圖片描述

動態代理類並不是程式設計師寫的,而是根據傳入的引數,由Proxy類在執行時生成的,所以可以傳入不同的引數,這樣就可以在執行時產生不同的代理類,所以是動態的。

// InvocationHandler實現類,裡面有一個object物件,指向真正的實現類
InvocationHandler handler = new MyInvocationHandler();
// 代理類,是有Proxy生成的,根據這點程式碼,已知的是,它實現了被代理類的介面,而且它有個引數為InvocationHandler作為引數的建構函式
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo)proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);

使用時,一般按下面的寫法:

Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[]{Foo.class}, handler);

再多寫一句,spring的AOP(面向切面程式設計)就是用的動態代理實現的,可以用於日誌,許可權控制,快取等,可以在InvocationHandler中的invoke方法內部呼叫實際方法前後加上一些有用的程式碼。