動態代理
1,動態代理的定義
顧名思義,動態代理既是起到動態的生成代理類的作用。代理類在程序運行時創建的代理方式被成為 動態代理。也就是說,這種情況下,代理類并不是在Java代碼中定義的,而是在運行時根據我們在Java代碼中的“指示”動態生成的。
2,動態代理的使用
(1)InvocationHandler接口在使用動態代理時,我們需要定義一個位于代理類與委托類之間的中介類,這個中介類被要求實現InvocationHandler接口,這個接口的定義如下:
1 public interface InvocationHandler {
2
3 Object invoke(Object proxy, Method method, Object[] args);
4
5 }
從InvocationHandler這個名稱我們就可以知道,實現了這個接口的中介類用做“調用處理器”。當我們調用代理類對象的方法時,這個“調用”會轉送到invoke方法中,代理類對象作為proxy參數傳入,參數method標識了我們具體調用的是代理類的哪個方法,args為這個方法的參數。這樣一來,我們對代理類中的所有方法的調用都會變為對invoke的調用,這樣我們可以在invoke方法中添加統一的處理邏輯(也可以根據method參數對不同的代理類方法做不同的處理)。因此我們只需在中介類的invoke方法實現中輸出“before”,然后調用委托類的invoke方法,再輸出“after”。下面我們來一步一步具體實現它。
(2)委托類的定義動態代理方式下,要求委托類必須實現某個接口,這里我們實現的是Sell接口。委托類Vendor類的定義如下:
1 public class Vendor implements Sell {
2
3 public void sell() {
4
5 System.out.println("In sell method");
6
7 }
8
9 public void ad() {
10
11 System,out.println("ad method")
12
13 }
14
15 }
(3)中介類上面我們提到過,中介類必須實現InvocationHandler接口,作為調用處理器”攔截“對代理類方法的調用。中介類的定義如下:
1 public class DynamicProxy implements InvocationHandler {
2
3 private Object obj;
4 //obj為委托類對象;
5
6 public DynamicProxy(Object obj) {
7
8 this.obj = obj;
9
10 }
11
12
13 @Override
14 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
15
16 System.out.println("before");
17
18 Object result = method.invoke(obj, args);
19
20 System.out.println("after");
21
22 return result;
23 }
24
25 }
從以上代碼中我們可以看到,中介類持有一個委托類對象引用,在invoke方法中調用了委托類對象的相應方法(第11行),看到這里是不是覺得似曾相識?通過聚合方式持有委托類對象引用,把外部對invoke的調用最終都轉為對委托類對象的調用。這不就是我們上面介紹的靜態代理的一種實現方式嗎?實際上,中介類與委托類構成了靜態代理關系,在這個關系中,中介類是代理類,委托類就是委托類; 代理類與中介類也構成一個靜態代理關系,在這個關系中,中介類是委托類,代理類是代理類。也就是說,動態代理關系由兩組靜態代理關系組成,這就是動態代理的原理。下面我們來介紹一下如何”指示“以動態生成代理類。
(4)動態生成代理類動態生成代理類的相關代碼如下:
1 public class Main {
2
3 public static void main(String[] args) {
4 //創建中介類實例
5
6 DynamicProxy inter =new DynamicProxy(new Vendor());
7
8 //加上這句將會產生一個$Proxy0.class文件,這個文件即為動態生成的代理類文件
9
10 System.getProperties().put( "sun.misc.ProxyGenerator.saveGeneratedFiles","true");
11 //獲取代理類實例sell
12
13 Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(),
new Class[] {Sell.class}, inter));
14
15 //通過代理類對象調用代理類方法,實際上會轉到invoke方法調用
16
17 sell.sell();
18
19 sell.ad();
20
21 }
22
23 }
24
25
在以上代碼中,我們調用Proxy類的newProxyInstance方法來獲取一個代理類實例。這個代理類實現了我們指定的接口并且會把方法調用分發到指定的調用處理器。這個方法的聲明如下:
復制代碼代碼如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException
方法的三個參數含義分別如下:
loader:定義了代理類的ClassLoder;
interfaces:代理類實現的接口列表
h:調用處理器,也就是我們上面定義的實現了InvocationHandler接口的類實例
3,動態代理的優缺點
優點:Java動態代理可以避免靜態代理帶來的代碼冗余的問題。
缺點:Java動態代理只能針對接口創建代理,不能針對類創建代理。