前言: 本文只是本人的學習總結,目的希望能和大家一起交流分享,順便備忘,如有不正確的地方,歡迎指正。本文可能需要你對webwork框架有一定的了解。
我們在開發中Proxy模式是經常用到的,代理主要用來對訪問的資源進行權限控制以及監控的目的,如果我們在開發系統需要對訪問的對象進行控制和監控的話,proxy模式是很有用途的。
舉些例子:
我們開發一個應用系統時,用戶請求任何頁面都要經過權限控制,最早開發的時候我們常常封裝一個權限驗證方法,然后在每個jsp或者對應的servlet中增加相關代碼,但是用戶對權限的需求往往是多變的,這樣一旦權限驗證方法變化(參數變化,增加方法),如果開發的系統很龐大的話,有可能你就需要修改幾百個jsp頁面或者servlet代碼。
還有我們常需要判斷session是否過期,如果過期就要重新登陸系統,如果每個頁面或者servlet都要加判斷代碼,那也是件比較痛苦的事情。
如果我們采用代理模式,增加Proxy對象,每次用戶請求必須通過proxy對象處理,由它專門處理相關權限控制,一旦權限需求變化了,只需要修改Proxy對象相關的實現方法。
Proxy模式不僅僅用于上述場景,還可以在其他方面應用。
我們可以研究研究webwork的源代碼,看看它是如何設計的。
webwork開發框架目前是比較流行的web開發框架之一,最近我的開發項目就采用了該框架,它相比struts有很多優點(晚出來的再沒優點也不行啊,呵呵,關于其缺點也有,有時間再說),
主要如下:
1、 易單元測試;
2、 線程安全;
3、 允許使用截取器模塊化前/后處理. 攔截器可以通過配置動態添加, 兩者之間沒有任何耦合;
4、 WebWork 2使用Ognl, 強大的表達式語言, 也可以訪問值棧. Ognl對集合和索引屬性的支持非常強大。
其中優點3的實現和proxy模式是非常相關的,下面就講講webwork如何采用Proxy模式實現其優點3的。
首先我們看看webwork的核心類ServletDispatcher的請求處理代碼:
public void serviceAction(
HttpServletRequest request, HttpServletResponse response,
String namespace, String actionName,
Map requestMap, Map parameterMap,

Map sessionMap, Map applicationMap)
{
HashMap extraContext = createContextMap(requestMap, parameterMap, sessionMap, applicationMap, request, response, getServletConfig());
extraContext.put(SERVLET_DISPATCHER, this);

try
{
ActionProxy proxy = ActionProxyFactory.getFactory).
createActionProxy(namespace, actionName, extraContext);
request.setAttribute("webwork.valueStack", proxy.getInvocation().getStack());
proxy.execute();
}

catch (ConfigurationException e)
{
log.error("Could not find action", e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
}

catch (Exception e)
{
log.error("Could not execute action", e);
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e);
}
}
.可以看到整個對請求的處理非常簡練:通過工廠方法獲取一個ActionProxy 實例,執行ActionProxy實例的execute()方法,所有請求都需要通過該方法處理。
ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);
這段代碼調用過程我簡單描述一下:
首先ActionProxyFactory.getFactory().獲取到一個DefaultActionProxyFactory實例,然后該工廠實例調用createActionProxy(相關參數)方法創建一個DefaultActionProxy示例。
下面我們看看這個DefaultActionProxy究竟是如何處理的:
首先它會根據相關信息獲取當前Action配置對象,從中可以知道當前Action中配置了那些攔截器、Result等等配置信息,具體可以查看ActionConfig對象,然后創建一個調用對象DefaultActionInvocation實例(也是通過工廠方法),該實例調用invoke()方法完成該action配置的攔截器的攔截方法以及action的execute()方法或action自定義的method的執行。

public String invoke() throws Exception
{

if (executed)
{
throw new IllegalStateException("Action has already executed");
}

if (interceptors.hasNext())
{
Interceptor interceptor = (Interceptor) interceptors.next();
resultCode = interceptor.intercept(this);

} else
{

if (proxy.getConfig().getMethodName() == null)
{
resultCode = getAction().execute();

} else
{
resultCode = invokeAction(getAction(), proxy.getConfig());
}
}
// this is needed because the result will be executed, then control will return to the Interceptor, which will
// return above and flow through again

if (!executed)
{

if (preResultListeners != null)
{
for (Iterator iterator = preResultListeners.iterator();

iterator.hasNext();)
{
PreResultListener listener = (PreResultListener) iterator.next();
listener.beforeResult(this, resultCode);
}
}

// now execute the result, if we're supposed to

if (proxy.getExecuteResult())
{
executeResult();
}
executed = true;
}
return resultCode;
}

而一般攔截器對象都是AroundInterceptor的子類,在AroundInterceptor類中的攔截方法如下:

public String intercept(ActionInvocation invocation) throws Exception
{
String result = null;
before(invocation);
result = invocation.invoke();
after(invocation, result);
return result;
}

注意該調用對象的invoke方法比較有意思,它采用的是遍歷調用的方式,每個Action一般都有多個攔截器,每個攔截器執行完畢后再回調該調用對象的invoke方法,有點像鏈式,如果中間有自定義攔截器有發現異常,不要再執行下去,直接返回Result相關字符串,中斷之后的攔截器以及Action不再執行,正常情況下鏈尾是調用對應Action實例的execute()方法,獲取Result相關字符串后,根據字符串值獲取相關Result實例,執行Result實例中excute()方法派發或者重導向到相關視圖(jsp、vm等等),一旦鏈尾處理過請求后,鏈中的其他節點就不需要再派發。只需繼續執行攔截器中的after()方法(如果有的話)的執行,webwork采用這樣的方式實現主要是為了滿足action后處理功能的需要(有點跑題了,變成webwork框架源碼分析)。
題外話:從整個調用過程我們可以發現: Webwork框架的核心功能實際上都是在Xwork框架中實現的,Webwork實際上只是Xwork在B/S系統上的應用。
結束總結:
1、我們在開發時,如果要對訪問的對象進行統一預處理、控制、監控管理時可以采用Proxy模式。
2、Proxy模式往往和Factory模式一起使用。個人理解是因為考慮系統的擴展性、通用性,有可能有不同的類型的Proxy以及調用,根據不同的應用場景,可以采用不同的工廠創建。
3、如果運用的不是很恰當的話,會造成Proxy的實現很龐大,并且和相關對象耦合過高,而webwork采用配置每個action對應的攔截器這種設計就非常好,耦合也比較低,實際上它變相的實現了每個對象采用不同的Proxy,個人感覺其這方面的設計很不錯,可以借鑒。