Struts2請求處理流程及源碼分析

1.1 Struts2請求處理

1. 一個請求在Struts2框架中的處理步驟:html

a) 客戶端初始化一個指向Servlet容器的請求;程序員

b) 根據Web.xml配置,請求首先通過ActionContextCleanUp過濾器,其爲可選過濾器,這個過濾器對於Struts2和其餘框架的集成頗有幫助(SiteMesh Plugin),主要清理當前線程的ActionContext和Dispatcher;web

c) 請求通過插件過濾器,如:SiteMesh、etc等過濾器;app

d) 請求通過核心過濾器FilterDispatcher,執行doFilter方法,在該方法中,詢問ActionMapper來決定這個請求是否須要調用某個Action;框架

e) 若是ActionMapper決定須要調用某個Action,則ActionMapper會返回一個ActionMapping實例(存儲Action的配置信息),並建立ActionProxy(Action代理)對象,將請求交給代理對象繼續處理;jsp

f) ActionProxy對象根據ActionMapping和Configuration Manager詢問框架的配置文件,找到須要調用的Action類;ide

g) ActionProxy對象建立時,會同時建立一個ActionInvocation的實例;源碼分析

h) ActionInvocation實例使用命名模式來調用,在調用Action的過程先後,涉及到相關攔截器(Intercepter)的調用;spa

i) 一旦Action執行完畢,ActionInvocation實例負責根據struts.xml中的配置建立並返回Result。Result一般是一個須要被表示的JSP或者FreeMarker的模版,也多是另外的一個Action鏈;插件

j) 若是要在返回Result以前作些什麼,能夠實現PreResultListener接口,PreResultListener能夠在Interceptor中實現,也能夠在Action中實現;

k) 根據Result對象信息,生成用戶響應信息response,在生成響應過程當中可使用Struts2 框架中繼承的標籤,在此過程當中仍會再次涉及到ActionMapper;

2. Struts2請求處理示意圖:

clip_image002

1.2 Struts2請求處理源碼分析

當用戶向Struts2發送請求時,FilterDispatcher的doFilter()方法自動調用,doFilter()方法處理請求過程,以下:

1. 建立值棧對象stack;

2. 建立Action上下文對象;

3. 對請求進行從新封裝,這次封裝根據請求內容的類型不一樣,返回不一樣的對象:

若是爲multipart/form-data類型,則返回MultiPartRequestWrapper類型的對象,該對象服務於文件上傳,不然返回StrutsRequestWrapper類型的對象,MultiPartRequestWrapper是StrutsRequestWrapper的子類,而這兩個類都是HttpServletRequest接口的實現。

4. 經過actionMapper.getMapping()得到ActionMapping對象,Action的配置信息存儲在ActionMapping對象中(Action的配置信息:Action的name、namespace和要調用的方法method)。相關代碼以下圖所示:

clip_image004

以上代碼,活動圖以下:

clip_image006

5. 若是getMapping()方法返回ActionMapping對象爲null,則FilterDispatcher認爲用戶請求不是Action,此時FilterDispatcher會首先分析:

若是請求以/struts開頭,會自動查找在web.xml文件中配置的packages初始化參數,FilterDispatcher會將packages參數值包下的文件看成靜態資源處理,即直接在頁面上顯示文件內容。

若是用戶請求的資源不是以/struts開頭—多是.jsp文件,也多是.html文件,則經過過濾器鏈繼續往下傳送,直到到達請求的資源爲止。

6. 若是getMapping()方法返回有效的ActionMapping對象,則被認爲正在請求某個Action,將調用Dispatcher.serviceAction(request, response, servletContext, mapping)方法。

以上六步,相關代碼以下圖所示:

clip_image008

clip_image010

以上代碼,活動圖以下:

clip_image012

7. 請求進入dispatcher.serviceAction(request,response,servletContext,mapping)方法中:

a) 將相關對象信息封裝爲Map(如:HttpServletRequest、Http parameters、HttpServletResponse、HttpSession、ServletContext、ActionMapping等對象信息),並存入到執行上下文Map中,返回執行上下文Map對象extraMap;

b) 獲取mapping對象中存儲的action命名空間、name屬性、method屬性等信息;

c) 加載並解析Struts2配置文件,若是沒有人爲配置,默認按順序加載struts-default.xml、struts-plugin.xml、struts.xml,將action配置、result配置、interceptor配置,解析並存入至config對象中,返回文件配置對象config;

d) 根據執行上下文Map、action命名空間、name屬性、method屬性等建立用戶Action的代理對象;

e) 執行Action代理對象proxy.execute()方法,並轉向結果;

以上步驟相關代碼,如圖所示:

clip_image014

8. 執行Action代理對象proxy.execute()方法,該方法的執行,其實就是調用了invocation.invoke()方法,以下圖所示:

clip_image016

9. 執行invocation.invoke()方法,實現了截攔器的遞歸調用和執行Action的execute()方法,DefaultActionInvocation.invoke()方法中代碼,以下圖所示:

clip_image018

在以上代碼中,並未看出攔截器的遞歸調用,實際上是否遞歸調用,是由程序員來控制的,遞歸調用實現很簡單:

a) 首先看下Interceptor接口定義:

clip_image020

b) 全部的截攔器必須實現intercept方法,而該方法的參數偏偏又是ActionInvocation,因此若是在intercept方法中調用invocation.invoke(),則會繼續從Action的Intercepor列表中找到下一個截攔器執行,依此遞歸調用Intercepor;

Struts2中的日誌攔截器LoggingInterceptor,以下圖所示:

clip_image022

c) 攔截器遞歸調用活動圖,以下所示:

clip_image024

10. 在invocation.invoke()方法中,執行攔截器、action並得到resultCode完畢後,則會繼續執行PreResultListener集合,並生成Result對象,實現PreResultListener接口,可在返回Result以前,作些自定義處理,如圖所示:

clip_image026

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

clip_image028

clip_image030

以上兩種方式,你們能夠發現都是經過匿名內部類的方式實現,其實還有一種方式就是經過在攔截器中實現PreResultListener接口,並實現方法beforeResult方法,便可。以下圖所示:

clip_image032

11. 最後,經過生成Result完成用戶響應;

以上1-11步,爲Struts2處理請求的完整流程分析,其相關代碼調用流程,以下圖所示:

clip_image034

相關文章
相關標籤/搜索