代理模式是我們比較常用的設計模式之一。其中新思想是為了提供額外的處理或者不同的操作而在實際對象與調(diào)用者之間插入一個代理對象。這些額外的操作通常需要與實際對象進行通信,代理模式一般涉及到的角色有:
抽象角色:聲明真實對象和代理對象的共同接口;
代理角色:代理對象角色內(nèi)部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的接口以便在任何時刻都能代替真實對象。同時,代理對象可以在執(zhí)行真實對象操作時,附加其他的操作,相當于對真實對象進行封裝。
真實角色:代理角色所代表的真實對象,是我們最終要引用的對象。
以下以發(fā)送消息為例來說明一個簡單的代理模式的基本實現(xiàn):
首先明確目的:有一條消息,需要把這個消息發(fā)送出去,根據(jù)這個目的定義對應接口MessageHandler。需要的附加操作:假設需要驗證消息的長度不能超過指定長度并且不能為空,并且我們需要統(tǒng)計相關信息發(fā)送到次數(shù),超過指定的次數(shù)我們需要輸出警報。我們通過代理模式來實現(xiàn)這個附加的操作。下面為對應的類關系圖及示例代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
//接口定義 public interface MessageHandler { public void sendMessage(String msg); } //通過Email方式發(fā)送消息的實現(xiàn)類 public class EmailMessage implements MessageHandler { @Override public void sendMessage(String msg) { // TODO Auto-generated method stub System.out.println(msg+ " send!!" ); } } //消息處理的代理類 public class MessageProxy implements MessageHandler { private static int count; private MessageHandler emailMsg; @Override public void sendMessage(String msg) { // TODO Auto-generated method stub if (checkMessage(msg)) { if (emailMsg== null ) emailMsg= new EmailMessage(); count++; emailMsg.sendMessage(msg); System.out.println( "Message sent:" +count); } } private boolean checkMessage(String msg) { return msg != null && msg.length() > 10 ; } } //調(diào)用類 public class MainClass { private static void runProxy(MessageHandler handler) { handler.sendMessage( "message for test" ); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub runProxy( new EmailMessage()); System.out.println( "++++++++++++++++Pjroxy++++++++++++++++++" ); runProxy( new MessageProxy()); } } //輸出 message for test send!! ++++++++++++++++Pjroxy++++++++++++++++++ message for test send!! Message sent: 1 |
在例子中我們可以方便的在消息發(fā)送過程中添加各種需要的附加處理方式,也能方便的替換消息的處理方式,如將通過Email發(fā)送消息替換為通過短信發(fā)送消息,而調(diào)用方不會有絲毫察覺!在任何你想要將一些額外操作分離到具體對象之外,特別是希望能夠很容易做出修改,或者想在具體對象的方法執(zhí)行前插入一些額外操作的時候,代理就顯得十分有用!
動態(tài)代理
Java中動態(tài)代理機制的引入使得代理模式的思想更加完善與進步,它允許動態(tài)的創(chuàng)建代理并支持對動態(tài)的對所代理的方法進行調(diào)用。Java動態(tài)代理類位于Java.lang.reflect包下,一般主要涉及到以下兩個類:
(1). Interface InvocationHandler:該接口中僅定義了一個方法Object:invoke(Object obj,Method method, Object[] args)。在實際使用時,第一個參數(shù)obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數(shù)數(shù)組。這個抽象方法在代理類中動態(tài)實現(xiàn)。
(2).Proxy:該類即為動態(tài)代理類,作用類似于上例中的ProxySubject,其中主要包含以下內(nèi)容:
Protected Proxy(InvocationHandler h):構(gòu)造函數(shù),估計用于給內(nèi)部的h賦值。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces):獲得一個代理類,其中l(wèi)oader是類裝載器,interfaces是真實類所擁有的全部接口的數(shù)組。
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實例,返回后的代理類可以當作被代理類使用(可使用被代理類的在Subject接口中聲明過的方法)。
所謂Dynamic Proxy是這樣一種class:它是在運行時生成的class,在生成它時你必須提供一組interface給它,然后該class就宣稱它實現(xiàn)了這些interface。你當然可以把該class的實例當作這些interface中的任何一個來用。當然啦,這個Dynamic Proxy其實就是一個Proxy,它不會替你作實質(zhì)性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。 下面我們通過動態(tài)代理來重新實現(xiàn)上面發(fā)送信息的例子!
在上面的例子基礎上,我們先添加一個通過短信來發(fā)送消息的處理類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
public class SmsMessage implements MessageHandler { @Override public void sendMessage(String msg) { // TODO Auto-generated method stub System.out.println( "SMS Message :" + msg+ " sent !" ); } } //動態(tài)代理類 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicMessageProxy implements InvocationHandler { private static int count; private MessageHandler msgHandler; public DynamicMessageProxy(MessageHandler handler) { msgHandler = handler; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println( "++++++++=============+++++++++" ); System.out.println( "proxy:" + proxy.getClass()); System.out.println( "method:" + method); System.out.println( "++++++++=============+++++++++" ); if (args != null && args.length == 1 && checkMessage((String) args[ 0 ])) { count++; System.out.println( "Message sent:" + count); return method.invoke(msgHandler, args); } return null ; } private boolean checkMessage(String msg) { return msg != null && msg.length() > 10 ; } } //下面是調(diào)用 import java.lang.reflect.Proxy; public class MainClass { private static void runProxy(MessageHandler handler) { handler.sendMessage( "message for test" ); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub // runProxy(new EmailMessage()); // System.out.println("++++++++++++++++Proxy++++++++++++++++++"); // runProxy(new MessageProxy()); MessageHandler handler = new EmailMessage(); runProxy(handler); MessageHandler proxy = (MessageHandler) Proxy.newProxyInstance( MessageHandler. class .getClassLoader(), new Class[] { MessageHandler. class }, new DynamicMessageProxy( handler)); runProxy(proxy); System.out.println( "++++++++++++++++++++++++++++++++++" ); // 短信方式 handler = new SmsMessage(); runProxy(handler); proxy = (MessageHandler) Proxy.newProxyInstance(MessageHandler. class .getClassLoader(), new Class[] { MessageHandler. class }, new DynamicMessageProxy(handler)); runProxy(proxy); } } //下面為以上方法的輸出: message for test send!! ++++++++=============+++++++++ proxy: class $Proxy0 method: public abstract void MessageHandler.sendMessage(java.lang.String) ++++++++=============+++++++++ Message sent: 1 message for test send!! ++++++++++++++++++++++++++++++++++ SMS Message :message for test sent ! ++++++++=============+++++++++ proxy: class $Proxy0 method: public abstract void MessageHandler.sendMessage(java.lang.String) ++++++++=============+++++++++ Message sent: 2 SMS Message :message for test sent ! |
以上例子中,通過調(diào)用Proxy.newProxyInstance方法創(chuàng)建動態(tài)代理對象,該方法需要傳入一個 類加載器、一組希望代理實現(xiàn)的接口列表、InvocationHandler 接口的一個具體實現(xiàn)。動態(tài)代理可以將所有調(diào)用重定向到調(diào)用處理器,通常我們會向該處理器傳遞一個時間對象的引用。invoke()方法中傳遞進來了代理對象,當你需要區(qū)分請求來源時這是非常有用的,例如你可以通過判斷傳入的方法名屏蔽掉某些方法的執(zhí)行!動態(tài)代理機制并不是會很頻繁使用的方法,它通常用來解決一些特定情況下的問題,因此不要盲目的為了使用而使用,要根據(jù)自己的實際需求來決定!
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。