![](http://static.javashuo.com/static/loading.gif)
這張圖來自於Struts2的Reference,咱們可以在圖中看到許多咱們不熟悉的名詞,好比ActionProxy,Interceptor等等。這些都是Struts2的Control層的重要元素,也是Struts2的Control層的一個層次化的體現。
上面的這張圖基本上可以歸納了Struts2的整個生命週期。接下來,咱們就對Action中的一些重要元素進行簡單的描述。
1. Action映射:
action映射是Struts2框架中的基本」 工做單元」,action映射就是將一個請求URL(即action的名字)映射到一個action類,當一個請求匹配某個action的名字時,框架就使用這個映射來肯定如何處理請求。html
2. 使用method屬性
在配置action時,咱們能夠經過action元素的method屬性來指定action調用的
方法,所指定的方法,必須遵循與execute方法相同的格式。
在Struts2.xml文件中,咱們能夠爲同一個action類配置不一樣的別名,並使用
method屬性。
在Struts.xml文件中爲同一個Action類配置不一樣的別名
<!-- 使用method屬性 -->java
<struts>
<!--
<constant name="struts.enable.DynamicMethodInvocation" value="false" />設計模式
<include file="example.xml"/>安全
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">HelloWorld</param>
<param name="namespace">/example</param>
</result>
</action>
</package>
-->
<!-- Add packages here -->
<constant name="struts.devMode" value="true" />
<package name="front" extends="struts-default" namespace="/">
<action name="index" class="com.bjsxt.struts2.front.action.IndexAction1">
<result name="success">/ActionIntroduction.jsp</result>
</action>
</package>app
</struts>框架
對應的MethodAction類,代碼以下:jsp
package com.bjsxt.struts2.front.action;ide
public class IndexAction1 {
public String execute() {
return "success";
}
}函數
3.動態方法調用:post
另一種無需配置就能夠直接調用Action中的非execute方法的方式,是使用
Struts2的動態方法調用。
動態方法調用是在action的名字中使用感嘆號(!)來標識要調用的方法名,其語法格
式爲 actionName!methodName.action
Action的介紹
多數的MVC框架中的Control層,都是一個Java對象。按照慣例,咱們一般會把這個層次上面的Java對象統稱爲Action層。本篇文章,咱們就來簡單介紹一下Struts2中Action的相關內容。
傳統的MVC框架中,Control層通常都是一個相似與Servlet的一個Java對象。由於從職責上講,Control層須要完成如下的職責:
1. 接收從Web容器傳遞過來的參數,並作恰當的類型轉化
2. 調用邏輯處理
3. 蒐集數據,並返回到視圖
而在這個其中的第一步和第三步,都離不開Web容器中的對象的處理。
Struts2中的Action,與其餘傳統的MVC框架不一樣,使用了XWork的Action來構造Control層。讓咱們首先來看看Action的接口定義:
咱們只須要實現這個接口,就能夠在其中編寫業務邏輯完成咱們的功能。
在這個接口定義中,咱們能夠明顯看到與傳統的MVC框架之間的區別:Struts2中的Action,並不須要依賴於特定的Web容器。咱們看不到相似HttpServletRequest,HttpServletResponse等Web容器相關的對象。
而這一點,也帶來了問題:
提問:Struts2的Action並不帶有任何Web容器相關的對象,Action又是如何工做在Web容器中的呢?
雖然Struts2的Action只是一個很是普通的Java對象,並不具有任何Web容器的特質,可是咱們須要把Action放到一個更加大的環境中來看。事實上,Struts2爲Action的執行,準備了完整的數據環境和執行環境。而這個執行環境,就保證了Action在Web容器中的順利運行。
在Struts2中,每一個Http的請求,會被髮送到一個Filter。而這個Filter,就會針對每一個請求,建立出一個代碼的執行環境,並在這個基礎上,爲每一個執行環境配備與之對應的數據環境,這個數據環境中的內容,就來自於Web容器中的一個又一個對象。這樣,就可以順利調用Action執行代碼而無需擔憂它是否運行在Web容器中了。
至於這個執行環境和數據環境究竟是什麼,咱們接下來會詳細講到。
提問:Struts2的Action並不帶有任何Web容器相關的對象,Action中又如何與Web容器進行通訊並獲取Web容器的相關對象呢?
剛剛咱們提到Struts2會爲每一個Http的請求創建一個執行環境和數據環境。其中,數據環境就成爲了Action獲取Web容器的基礎。因此,當Action須要獲取Web容器的相關對象,須要經過數據環境來進行。
Struts2的Action的這一個重要特性,至少能爲咱們帶來如下好處:
1. 使得Struts2的Action很是易於測試
若是咱們徹底不考慮Action的執行環境,僅僅把Action看做一個普通的Java對象,那麼咱們甚至能夠直接new一個Action的對象,經過執行其中的方法完成測試。這樣,咱們就不須要任何的Mock,來模擬Web容器的環境。
2. 結合Action的執行環境,使得Struts2在Control這個層次上,可以定義更加豐富的執行層次
由於Action是一個普通的Java類,而不是一個Servlet類,徹底脫離於Web容器,因此咱們就可以更加方便地對Control層進行合理的層次設計,從而抽象出許多公共的邏輯,並將這些邏輯脫離出Action對象自己。事實上,Struts2也正是這麼作的,不管是Interceptor,仍是Result,其實都是抽象出了Action中公共的邏輯部分,將他們放到了Action的外面,從而更加簡化了Action的開發。
3. 使得Struts2的Action看上去更像一個POJO,從而更加易於管理
Struts2的Action是一個線程安全的對象。而Web容器傳遞過來的參數,也會傳遞到Action中的成員變量中。這樣,Action看上去就更像一個POJO,從而可以方便的被許多對象容器進行管理。好比說,你能夠很是方便得把Action歸入到Spring的容器中進行管理。
在大概瞭解了Struts2的Action後,咱們來重點研究一下在Struts2的Action周圍,爲Action進行服務的一些重要元素,這些元素將涵蓋Action的數據環境,Action的執行環境、Action的調度者、Action的層次結構和Action的執行結果。
ActionContext —— 數據環境
以前咱們提到了Struts2的Action並非一個Servlet,它是脫離了Web容器的。可是對於一個Web框架來講,全部的數據請求(Request)和數據返回(Response)都來源於Web容器,那麼Action在執行的時候,如何去獲取這些數據呢?
這個問題的答案就在於,咱們須要爲每一個Action準備一個數據環境,這個數據環境被稱之爲:ActionContext。因爲Action是應對於一個又一個的URL請求,因此ActionContext應該具有如下的特性:
1. ActionContext應成爲Action與Web容器之間的橋樑
2. ActionContext中應該保存有針對某個請求的詳細信息
3. ActionContext應該是一個線程安全的類對象
Interceptor —— 豐富的層次結構
簡單回顧一下上面所提到的Action的職責,咱們看到,須要在Action這個層面上完成的事情還很多。而完成這些職責,就須要咱們對這些職責進行合理的分類和排序,將他們組織成有序的執行隊列。在Struts2中,使用了一種相似責任鏈的設計模式對這些不一樣的職責進行分類並串聯起來,從而使得Action層具有了豐富的層次結構。而在這個執行隊列中的每一個元素,就被咱們稱之爲Interceptor,也就是攔截器。
攔截器是AOP中的概念,它自己是一段代碼,能夠經過定義「織入點」,來指定攔截器的代碼在「織入點」的先後執行,從而起到攔截的做用。正如上面Struts2的Reference中講述的,Struts2的Interceptor,其攔截的對象是Action代碼,能夠定義在Action代碼以前或者以後執行攔截器的代碼。
若是仔細留意一下Action LifeCycle圖中的Interceptor和Action的部分,咱們能夠看到,Interceptor一層一層的把Action包了起來。這是一個典型的堆棧結構,在代碼執行的時候,每一個Interceptor不只須要文成它自身的邏輯,還經過遞歸調用負責下一個攔截器或Action的調用。
也正如Struts2的Reference所說,Struts2提供的絕大多數的功能支持,都經過Interceptor來實現,這些Interceptor能夠隨意進行配置,而且可以方便的插入到程序中去運行。
Result —— 執行結果
有執行就必然有執行的結果。在Struts2中,Action的執行結果被抽象成了一個層次。在這個層次中,能夠定義任意類型的View層的結構。也就是說,Struts2並不強制View層的表現形式,能夠是JSP、Freemarker模板、二進制流輸出等等。
Struts2把執行結果抽象成一個層次,使得你能夠再也不關注許多視圖整合上面的細節,只須要考慮視圖的類型和數據的準備,這樣,你也能夠沒必要在沉浸在雜亂的構造視圖的代碼中。
ActionProxy —— 執行環境
有了攔截器Interceptor,有了Action自己,也有了Action的執行結果Result,咱們就須要一個相似調度器的產品,將這些元素整合起來,進行調度執行。在上面的Action Lifecyle的圖中,咱們能夠看到,Interceptor、Action和Result都處於ActionProxy中,因此ActionProxy就成爲了全部這些元素的執行環境。
既然是執行環境,那麼ActionProxy就須要提供Action執行的時候一切所須要的配置、參數等等,固然,也要有進行Action調用的入口。因此讓咱們來看一下ActionProxy的接口:
很顯然,在這其中,prepare和execute方法是用做Action調用的入口函數,其餘的接口定義都與Action執行時的運行參數和配置有關。
ActionInvocation —— 調度者
在上面的ActionProxy的接口定義中,咱們能夠看到有一個比較特殊的變量:ActionInvocation比較吸引咱們的眼球。從字面上去理解,ActionInvocation就是Action的調用者。事實上也是如此,ActionInvocation在這個Action的執行過程當中,負責Interceptor、Action和Result等一系列元素的調度。在以後的章節中,這個ActionInvocation類也將成爲咱們解讀Struts2源碼的一個重要入手點。這個類將告訴你,Struts2是如何經過ActionInvocation來實現對Interceptor、Action和Result的合理調度的。