1. Struts2與MVC
-
1.1 基本的MVC架構(gòu)
模型-視圖-控制器或通常被稱為 MVC裁僧,是一種用于開(kāi)發(fā)web應(yīng)用程序的軟件設(shè)計(jì)模式.MVC模式由以下三個(gè)部分組成:
Model:模式的最低層,負(fù)責(zé)維護(hù)數(shù)據(jù).
View:負(fù)責(zé)顯示全部或部分的數(shù)據(jù)給用戶.
Controller: 控制模型和視圖之間的交互的軟件代碼.
MVC抽象用圖形表示如下:
-
1.2 Struts2的MVC
Struts2是一個(gè)pull-MVC(或 MVC2)框架滩报。Struts 2 的模型-視圖-控制器模式由下面的五個(gè)核心部件實(shí)現(xiàn):
** 動(dòng)作,攔截器,值棧/OGNL,結(jié)果/結(jié)果類型,視圖技術(shù)**.
控制器是由 Struts2調(diào)度servlet過(guò)濾器和攔截器實(shí)現(xiàn)的.
模型是由Action(包括ActionForm)實(shí)現(xiàn)的.
視圖是由JSp,FreeMaker等技術(shù)實(shí)現(xiàn)的,抽象成結(jié)果.
值棧/OGNL,結(jié)果/結(jié)果類型提供共同主線,連接和集成其他組件.
2. Struts架構(gòu)
這張圖很好地反應(yīng)了Struts2的整個(gè)框架.
FilterDispatcher:整個(gè)Struts2的調(diào)度中心(現(xiàn)在用StrutsPrepareAndExecuteFilter)锅知,也就是MVC中的C(控制中心),根據(jù)ActionMapper的結(jié)果來(lái)決定是否處理請(qǐng)求脓钾,如果ActionMapper指出該URL應(yīng)該被Struts2處理售睹,那么它將會(huì)執(zhí)行Action處理,并停止過(guò)濾器鏈上還沒(méi)有執(zhí)行的過(guò)濾器.
ActionMapper:會(huì)判斷這個(gè)請(qǐng)求是否應(yīng)該被Struts2處理可训,如果需要Struts2處理昌妹,ActionMapper會(huì)返回一個(gè)對(duì)象來(lái)描述請(qǐng)求對(duì)應(yīng)的ActionInvocation的信息.
ActionProxy:它會(huì)創(chuàng)建一個(gè)ActionInvocation代理實(shí)例,位于Action和xwork之間握截,使得我們?cè)趯?lái)有機(jī)會(huì)引入更多的實(shí)現(xiàn)方式飞崖,比如通過(guò)WebService來(lái)實(shí)現(xiàn)等.
ConfigurationManager:是xwork配置的管理中心,通俗的講谨胞,可以把它看做struts.xml這個(gè)配置文件在內(nèi)存中的對(duì)應(yīng).
struts.xml是Stuts2的應(yīng)用配置文件固歪,負(fù)責(zé)諸如URL與Action之間映射的配置、以及執(zhí)行后頁(yè)面跳轉(zhuǎn)的Result配置等.
ActionInvocation:真正調(diào)用并執(zhí)行Action胯努,它擁有一個(gè)Action實(shí)例和這個(gè)Action所依賴的攔截器實(shí)例.ActionInvocation會(huì)執(zhí)行這些攔截器牢裳、Action以及相應(yīng)的Result.類似于調(diào)度器.
Interceptor(攔截器):攔截器是一些無(wú)狀態(tài)的類,攔截器可以自動(dòng)攔截Action叶沛,它們給開(kāi)發(fā)者提供了在Action運(yùn)行之前或Result運(yùn)行之后來(lái)執(zhí)行一些功能代碼的機(jī)會(huì)蒲讯。類似于我們熟悉的javax.servlet.Filter.
Action:動(dòng)作類是Struts2中的動(dòng)作執(zhí)行單元。用來(lái)處理用戶請(qǐng)求灰署,并封裝業(yè)務(wù)所需要的數(shù)據(jù).
Result:Result就是不同視圖類型的抽象封裝模型判帮,不同的視圖類型會(huì)對(duì)應(yīng)不同的Result實(shí)現(xiàn),Struts2中支持多種視圖類型溉箕,比如Jsp晦墙,F(xiàn)reeMarker等.
Templates:各種視圖類型的頁(yè)面模板,比如JSP就是一種模板頁(yè)面技術(shù).
Tag Subsystem:Struts2的標(biāo)簽庫(kù)肴茄,它抽象了三種不同的視圖技術(shù)JSP晌畅、velocity、freemarker独郎,可以在不同的視圖技術(shù)中踩麦,幾乎沒(méi)有差別的使用這些標(biāo)簽.
3. Struts2運(yùn)行流程
-
用戶發(fā)出請(qǐng)求
請(qǐng)求會(huì)被Tomcat接收到,Tomcat服務(wù)器來(lái)選擇處理這個(gè)請(qǐng)求的Web應(yīng)用氓癌,就是選擇哪一個(gè)web工程來(lái)處理這個(gè)請(qǐng)求. -
web容器去讀這個(gè)工程的web.xml
在web.xml中進(jìn)行匹配,發(fā)現(xiàn)是由struts2的過(guò)濾器FilterDispatcher(StrutsPrepareAndExecuteFilter)來(lái)處理,找到該過(guò)濾器的實(shí)例(初始化). -
找到FilterDispatcher,回調(diào)doFilter()
通常情況下谓谦,web.xml文件中還有其他過(guò)濾器時(shí),F(xiàn)ilterDispatcher是放在濾器鏈的最后贪婉;如果在FilterDispatcher前出現(xiàn)了如SiteMesh這種特殊的過(guò)濾器反粥,還必須在SiteMesh前引用Struts2的ActionContextCleanUp過(guò)濾器. -
FilterDispatcher將請(qǐng)求轉(zhuǎn)發(fā)給ActionMapper
ActionMapper負(fù)責(zé)識(shí)別當(dāng)前的請(qǐng)求是否需要Struts2做出處理. -
ActionMapper告訴FilterDispatcher,需要處理這個(gè)請(qǐng)求,建立ActionProxy
FilterDispatcher會(huì)停止過(guò)濾器鏈以后的部分疲迂,所以通常情況下:FilterDispatcher應(yīng)該出現(xiàn)在過(guò)濾器鏈的最后才顿。然后建立一個(gè)ActionProxy對(duì)象,這個(gè)對(duì)象作為Action與xwork之間的中間層尤蒿,會(huì)代理Action的運(yùn)行過(guò)程. -
ActionProxy詢問(wèn)ConfigurationManager,讀取Struts.xml
ActionProxy對(duì)象剛被創(chuàng)建出來(lái)的時(shí)候郑气,并不知道要運(yùn)行哪個(gè)Action,它手里只有從FilterDispatcher中拿到的請(qǐng)求的URL.這時(shí)候,它問(wèn)ConfigurationManager問(wèn)到底要運(yùn)行哪個(gè)Action.
而ConfigurationManager就是負(fù)責(zé)讀取并管理struts.xml的腰池,可以簡(jiǎn)單的理解為ConfigurationManager是struts.xml在內(nèi)存中的映像.
在服務(wù)器啟動(dòng)的時(shí)候尾组,ConfigurationManager會(huì)一次性的把struts.xml中的所有信息讀到內(nèi)存里,并緩存起來(lái)示弓,以保證ActionProxy拿著來(lái)訪的URL向他詢問(wèn)要運(yùn)行哪個(gè)Action的時(shí)候讳侨,就可以直接匹配、查找并回答了. -
ActionProxy建立ActionInvocation對(duì)象
ActionProxy拿到了運(yùn)行哪個(gè)Action奏属、相關(guān)的攔截器以及所有可能使用的result信息跨跨,就可以著手建立ActionInvocation對(duì)象了,ActionInvocation對(duì)象描述了Action運(yùn)行的整個(gè)過(guò)程. -
在execute()之前的攔截器
在execute()之前會(huì)執(zhí)行很多默認(rèn)的攔截器.攔截器的運(yùn)行被分成兩部分囱皿,一部分在Action之前運(yùn)行勇婴,一部分在Result之后運(yùn)行,而且順序是剛好反過(guò)來(lái)的铆帽。也就是在Action執(zhí)行前的順序咆耿,比如是攔截器1、攔截器2爹橱、攔截器3萨螺,那么運(yùn)行Result之后,再次運(yùn)行攔截器的時(shí)候愧驱,順序就變成攔截器3慰技、攔截器2、攔截器1了组砚。 - 執(zhí)行execute()方法
- 根據(jù)execute方法返回的結(jié)果吻商,也就是Result,在struts.xml中匹配選擇下一個(gè)頁(yè)面
- 找到模版頁(yè)面,根據(jù)標(biāo)簽庫(kù)生成最終頁(yè)面
- 在execute()之后執(zhí)行的攔截器,和8相反
-
ActionInvocation對(duì)象執(zhí)行完畢
這時(shí)候已經(jīng)得到了HttpServletResponse對(duì)象了,按照配置定義相反的順序再經(jīng)過(guò)一次過(guò)濾器,向客戶端展示結(jié)果.
4. 再看Struts2架構(gòu)與流程
Struts2本身,也包含了真正意義上的Struts2與Xwork兩種框架.從職責(zé)上來(lái)說(shuō)糟红,XWork才是真正實(shí)現(xiàn)MVC的框架艾帐,Struts2的工作是在對(duì)Http請(qǐng)求進(jìn)行一定處理后乌叶,委托XWork完成真正的邏輯處理.將Web容器與MVC實(shí)現(xiàn)分離解耦,是Struts2的精妙之處
-
4.1 Struts2部分
在第二節(jié)的框架圖中,過(guò)濾器部分均屬于Struts2部分.
包括StrutsPrepareFilter與StrutsExecuteFilter,是StrutsPrepareAndExecuteFilter的兩部分.
前者是Struts2進(jìn)行Http請(qǐng)求的預(yù)處理,而后者是Struts2進(jìn)行Http請(qǐng)求的邏輯處理.
StrutsPrepareAndExecuteFilter自2.1.3替代FilterDispatcher.如果想自定義過(guò)濾器, 要放在strtus2過(guò)濾器之前,又想在執(zhí)行action之前拿filter做一些事,FilterDispatcher做不到.而StrutsPrepareAndExecuteFilter可以拆分成StrutsPrepareFilter和StrutsExecuteFilter,可以在這兩個(gè)過(guò)濾器之間加上自己的過(guò)濾器.
-
4.2 XWork部分
在離開(kāi)web容器以后,就到了Xwork部分,執(zhí)行業(yè)務(wù)邏輯.它包括了ActionProxy,ActionInvocation,Interceptor,Action,ActionContext,ValueStack,Result七個(gè)元素.
該圖很直觀地描繪了Xwork的整體架構(gòu)(不包括Dispatcher),包括了第二節(jié)架構(gòu)圖中的大部分,我們稱之為控制流元素.另外增加了ActionContext與ValueStack這樣的數(shù)據(jù)流元素.
關(guān)于控制流元素,在第二節(jié)已有相關(guān)介紹,接下來(lái)看看數(shù)據(jù)流元素的內(nèi)容:
ActionContext-數(shù)據(jù)環(huán)境
ActionContext是一個(gè)獨(dú)立的數(shù)據(jù)結(jié)構(gòu)柒爸,其主要作用是為XWork的執(zhí)行提供數(shù)據(jù)環(huán)境准浴。無(wú)論是請(qǐng)求的參數(shù),還是處理的返回值捎稚,甚至一些原生的Web容器對(duì)象乐横,都被封裝于ActionContext的內(nèi)部,成為了Struts2 / XWork執(zhí)行時(shí)所依賴的數(shù)據(jù)基礎(chǔ).職責(zé)在于數(shù)據(jù)存儲(chǔ).
ValueStack-數(shù)據(jù)訪問(wèn)環(huán)境
ValueStack本身是一個(gè)數(shù)據(jù)結(jié)構(gòu)今野,其主要作用是用以對(duì)OGNL計(jì)算進(jìn)行擴(kuò)展葡公。因而,位于ActionContext之中的ValueStack則賦予了ActionContext進(jìn)行數(shù)據(jù)計(jì)算的功能条霜,從而使得ValueStack自身成為了一個(gè)可以進(jìn)行數(shù)據(jù)訪問(wèn)的環(huán)境.職責(zé)在于數(shù)據(jù)傳輸.
-
4.3 Struts2初始化
第二節(jié)的圖對(duì)應(yīng)當(dāng)請(qǐng)求發(fā)生時(shí)候的過(guò)程.那么這一整個(gè)初始的框架是怎么產(chǎn)生的呢.其實(shí)在Struts2框架處理http請(qǐng)求,Xwork框架處理業(yè)務(wù)邏輯之前,還需要Struts2框架的初始化過(guò)程.
StrutsPrepareAndExecuteFilter是整個(gè)框架的入口點(diǎn),在初始化時(shí)候調(diào)用init()方法.
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();//類似一個(gè)工具類催什,包含了一些初始化操作
Dispatcher dispatcher = null;//Dispatcher:Struts2的核心分發(fā)器
try {
/**
* 封裝filterConfig,提供了一個(gè)便利的方法
* getInitParameterNames()蛔外,將枚舉類型的參數(shù)轉(zhuǎn)換成Iterator(EnumerationIterator)
*/
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);//初始化日志
//初始化Dispatcher
dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);//初始化靜態(tài)文件加載器
prepare = new PrepareOperations(dispatcher);//初始化HTTP預(yù)處理的操作類
execute = new ExecuteOperations(dispatcher);//初始化進(jìn)行HTTP請(qǐng)求處理的邏輯執(zhí)行操作類
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);//回調(diào)方法,留作用戶拓展
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}
可以看到大致的初始化過(guò)程分為以下幾步:
- 封裝FilterConfig->FilterHostConfig
- 初始化日志操作
- 初始化核心分發(fā)器Dispatcher
- 初始化靜態(tài)文件加載器,packages,該參數(shù)用來(lái)配置自動(dòng)搜尋目錄
- 初始化PrepareOperations和ExecuteOperations.
其中我們?cè)賮?lái)看核心分發(fā)器Dispatcher的初始化過(guò)程:
public void init() {
//初始化配置文件管理器
if (configurationManager == null) {
//根據(jù)name進(jìn)行對(duì)象尋址
//DEFAULT_BEAN_NAME = "struts"
//<bean type="org.apache.struts2.dispatcher.DispatcherErrorHandler" name="struts".../>
//<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
init_FileManager(); //初始化文件管理器
// 初始化Struct2的默認(rèn)配置加載器:
// org/apache/struts2/default.properties蛆楞,
// 如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫
init_DefaultProperties(); // [1]
//初始化Xml配置加載器:
// 如struts-default.xml,struts-plugin.xml,struts.xml
init_TraditionalXmlConfigurations(); // [2]
//初始化Properties配置加載器
init_LegacyStrutsProperties(); // [3]
//初始化用戶自定義的配置加載器
init_CustomConfigurationProviders(); // [5]
//初始化由web.xml傳入的參數(shù)
init_FilterInitParameters() ; // [6]
//初始化容器內(nèi)置的對(duì)象
//eg:ObjectFactory,FreemarkerManager....
init_AliasStandardObjects() ; // [7]
//創(chuàng)建容器, 初始化并預(yù)加載配置
Container container = init_PreloadConfiguration();
//對(duì)容器進(jìn)行依賴注入
container.inject(this);
//檢查對(duì)WebLogic的特殊支持
init_CheckWebLogicWorkaround(container);
//初始化所有的DispatcherListener
if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
//初始化錯(cuò)誤處理器
errorHandler.init(servletContext);
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
這里還可以分為四部:
- 初始化ConfigurationManager
- 初始化配置加載器.常用的配置文件加載順序如
a. default.properties
b. struts-default.xml
c. struts-plugin.xml
d. struts.xml
e. struts.properties
f. web.xml - 初始化容器(創(chuàng)建容器,依賴注入)
- 額外的初始化工作(初始化WebLogic配置,執(zhí)行DispatcherListner的配置..)
4.4 再看Struts2流程
根據(jù)上面的總結(jié),從一個(gè)更高的抽象角度看,我們把流程分為三部分,也即初始化部分,Struts2處理http請(qǐng)求部分,Xwork處理業(yè)務(wù)邏輯部分.