Java 代理模式詳解 – JAVA編程語言程序開發技術文章

代理模式是我們比較常用的設計模式之一。其中新思想是為瞭提供額外的處理或者不同的操作而在實際對象與調用者之間插入一個代理對象。這些額外的操作通常需要與實際對象進行通信,代理模式一般涉及到的角色有:

抽象角色:聲明真實對象和代理對象的共同接口;

代理角色:代理對象角色內部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的接口以便在任何時刻都能代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當於對真實對象進行封裝。

真實角色:代理角色所代表的真實對象,是我們最終要引用的對象。

 

以下以發送消息為例來說明一個簡單的代理模式的基本實現:

首先明確目的:有一條消息,需要把這個消息發送出去,根據這個目的定義對應接口MessageHandler。需要的附加操作:假設需要驗證消息的長度不能超過指定長度並且不能為空,並且我們需要統計相關信息發送到次數,超過指定的次數我們需要輸出警報。我們通過代理模式來實現這個附加的操作。下面為對應的類關系圖及示例代碼。

 

//接口定義
public interface MessageHandler {
 public void sendMessage(String msg);
}

// 通過Email方式發送消息的實現類
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;
 }
}

// 調用類
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

在例子中我們可以方便的在消息發送過程中添加各種需要的附加處理方式,也能方便的替換消息的處理方式,如將通過Email發送消息替換為通過短信發送消息,而調用方不會有絲毫察覺!在任何你想要將一些額外操作分離到具體對象之外,特別是希望能夠很容易做出修改,或者想在具體對象的方法執行前插入一些額外操作的時候,代理就顯得十分有用!

 

動態代理

java中動態代理機制的引入使得代理模式的思想更加完善與進步,它允許動態的創建代理並支持對動態的對所代理的方法進行調用。Java動態代理類位於Java.lang.reflect包下,一般主要涉及到以下兩個類:

(1). Interface InvocationHandler:該接口中僅定義瞭一個方法Object:invoke(Object obj,Method method, Object[] args)。在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數數組。這個抽象方法在代理類中動態實現。

(2).Proxy:該類即為動態代理類,作用類似於上例中的ProxySubject,其中主要包含以下內容:

Protected Proxy(InvocationHandler h):構造函數,估計用於給內部的h賦值。

Static Class getProxyClass (ClassLoader loader, Class[] interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部接口的數組。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實例,返回後的代理類可以當作被代理類使用(可使用被代理類的在Subject接口中聲明過的方法)。

 

所謂Dynamic Proxy是這樣一種class:它是在運行時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實現瞭這些interface。你當然可以把該class的實例當作這些interface中的任何一個來用。當然啦,這個Dynamic Proxy其實就是一個Proxy,它不會替你作實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。下面我們通過動態代理來重新實現上面發送信息的例子!

 

 

在上面的例子基礎上,我們先添加一個通過短信來發送消息的處理類:

public class SmsMessage implements MessageHandler {
@Override
public void sendMessage(String msg) {
// TODO Auto-generated method stub
System.out.println("SMS Message :" + msg+" sent !");
}
}
//動態代理類
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;
}
}
//下面是調用
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 !

以上例子中,通過調用Proxy.newProxyInstance方法創建動態代理對象,該方法需要傳入一個類加載器、一組希望代理實現的接口列表、InvocationHandler 接口的一個具體實現。動態代理可以將所有調用重定向到調用處理器,通常我們會向該處理器傳遞一個時間對象的引用。invoke()方法中傳遞進來瞭代理對象,當你需要區分請求來源時這是非常有用的,例如你可以通過判斷傳入的方法名屏蔽掉某些方法的執行!動態代理機制並不是會很頻繁使用的方法,它通常用來解決一些特定情況下的問題,因此不要盲目的為瞭使用而使用,要根據自己的實際需求來決定!

發佈留言