Struts請求處理
一個請求在Struts框架中的處理步驟
a) 客戶端初始化一個指向Servlet容器的請求
b) 根據Webxml配置請求首先經過ActionContextCleanUp過濾器其為可選過濾器這個過濾器對於Struts和其他框架的集成很有幫助(SiteMesh Plugin)主要清理當前線程的ActionContext和Dispatcher;
c) 請求經過插件過濾器如SiteMeshetc等過濾器
d) 請求經過核心過濾器FilterDispatcher執行doFilter方法在該方法中詢問ActionMapper來決定這個請求是否需要調用某個Action;
e) 如果ActionMapper決定需要調用某個Action則ActionMapper會返回一個ActionMapping實例(存儲Action的配置信息)並創建ActionProxy(Action代理)對象將請求交給代理對象繼續處理
f) ActionProxy對象根據ActionMapping和Configuration Manager詢問框架的配置文件找到需要調用的Action類
g) ActionProxy對象創建時會同時創建一個ActionInvocation的實例
h) ActionInvocation實例使用命名模式來調用在調用Action的過程前後涉及到相關攔截器(Intercepter)的調用
i) 一旦Action執行完畢ActionInvocation實例負責根據strutsxml中的配置創建並返回ResultResult通常是一個需要被表示的JSP或者FreeMarker的模版也可能是另外的一個Action鏈
j) 如果要在返回Result之前做些什麼可以實現PreResultListener接口PreResultListener可以在Interceptor中實現也可以在Action中實現
k) 根據Result對象信息生成用戶響應信息response在生成響應過程中可以使用Struts 框架中繼承的標簽在此過程中仍會再次涉及到ActionMapper;
Struts請求處理示意圖

Struts請求處理源碼分析
當用戶向Struts發送請求時FilterDispatcher的doFilter()方法自動調用doFilter()方法處理請求過程如下
創建值棧對象stack;
創建Action上下文對象
對請求進行重新封裝此次封裝根據請求內容的類型不同返回不同的對象
如果為multipart/formdata類型則返回MultiPartRequestWrapper類型的對象該對象服務於文件上傳否則返回StrutsRequestWrapper類型的對象MultiPartRequestWrapper是StrutsRequestWrapper的子類而這兩個類都是HttpServletRequest接口的實現
通過actionMappergetMapping()獲得ActionMapping對象Action的配置信息存儲在ActionMapping對象中(Action的配置信息Action的namenamespace和要調用的方法method)相關代碼如下圖所示

以上代碼活動圖如下

如果getMapping()方法返回ActionMapping對象為null則FilterDispatcher認為用戶請求不是Action此時FilterDispatcher會首先分析
如果請求以/struts開頭會自動查找在webxml文件中配置的packages初始化參數FilterDispatcher會將packages參數值包下的文件當作靜態資源處理即直接在頁面上顯示文件內容
如果用戶請求的資源不是以/struts開頭可能是jsp文件也可能是html文件則通過過濾器鏈繼續往下傳送直到到達請求的資源為止
如果getMapping()方法返回有效的ActionMapping對象則被認為正在請求某個Action將調用DispatcherserviceAction(request response servletContext mapping)方法
以上六步相關代碼如下圖所示


以上代碼活動圖如下

請求進入dispatcherserviceAction(requestresponseservletContextmapping)方法中
a) 將相關對象信息封裝為Map(如HttpServletRequestHttp parametersHttpServletResponseHttpSessionServletContextActionMapping等對象信息)並存入到執行上下文Map中返回執行上下文Map對象extraMap;
b) 獲取mapping對象中存儲的action命名空間name屬性method屬性等信息
c) 加載並解析Struts配置文件如果沒有人為配置默認按順序加載strutsdefaultxmlstrutspluginxmlstrutsxml將action配置result配置interceptor配置解析並存入至config對象中返回文件配置對象config;
d) 根據執行上下文Mapaction命名空間name屬性method屬性等創建用戶Action的代理對象
e) 執行Action代理對象proxyexecute()方法並轉向結果
以上步驟相關代碼
如圖所示

執行Action代理對象proxyexecute()方法該方法的執行其實就是調用了invocationinvoke()方法如下圖所示

執行invocationinvoke()方法實現了截攔器的遞歸調用和執行Action的execute()方法DefaultActionInvocationinvoke()方法中代碼如下圖所示

在以上代碼中並未看出攔截器的遞歸調用其實是否遞歸調用是由程序員來控制的遞歸調用實現很簡單
a) 首先看下Interceptor接口定義

b) 所有的截攔器必須實現intercept方法而該方法的參數恰恰又是ActionInvocation所以如果在intercept方法中調用invocationinvoke()則會繼續從Action的Intercepor列表中找到下一個截攔器執行依此遞歸調用Intercepor;
Struts中的日志攔截器LoggingInterceptor如下圖所示

c) 攔截器遞歸調用活動圖如下所示

在invocationinvoke()方法中執行攔截器action並獲得resultCode完畢後則會繼續執行PreResultListener集合並生成Result對象實現PreResultListener接口可在返回Result之前做些自定義處理如圖所示

在返回Result之前通過PreResultListener實現自定義處理常用的有兩種方式一種在Interceptor中實現一種在Action實現如圖所示


以上兩種方式大家可以發現都是通過匿名內部類的方式實現其實還有一種方式就是通過在攔截器中實現PreResultListener接口並實現方法beforeResult方法即可如下圖所示

最後通過生成Result完成用戶響應
以上步為Struts處理請求的完整流程分析其相關代碼調用流程如下圖所示

From:http://tw.wingwit.com/Article/program/Java/ky/201311/28036.html