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方法內部呼叫實際方法前後加上一些有用的程式碼。