流程介紹
我們模擬一個請求/響應的情景,來介紹Struts的工作流程。注意,下面的序號和圖中的序號沒有嚴格的對應關系。
- 瀏覽器向系統發出請求,請求的地址是ac.action
- 請求被StrutsPreparedExecuteFilter攔截,去掉.action后綴,所得結果ac作為action的name。
在Struts框架中,負責處理用戶請求的稱為action,這里的name用於獲取action,找到他,讓他干活。 - StrutsPreparedExecuteFilter在struts.xml中查找action映射相關的配置,根據ac查到action的類pkg.AcAction。
- StrutsPreparedExecuteFilter實例化pkg.AcAction,並調用其execute方法。
- pkg.AcAction工作完成之后,匯報工作結果:返回一個字符串success,稱之為result。
- StrutsPreparedExecuteFilter使用success去查struts.xml中的結果映射部分,獲取到對應的物理視圖資源是ac-success.jsp。
- StrutsPreparedExecuteFilter使用forward的方式,將ac-success.jsp展示給用戶。
仔細看一下上面的圖和內容,在腦海中回憶一下整個流程,最好能夠閉眼將整個流程復述一遍,然后再繼續。
我們的工作
在上述的流程描述中,幾乎都是框架要做的事,但是為了能讓框架順利的工作,我們要提供支持性的工作。使用框架基本都是這樣,我們按照框架的模式提供支持,框架自行工作。
接下來看一下我們需要做的事:
- 配置StrutsPreparedExecuteFilter。
StrutsPreparedExecuteFilter是整個流程中的核心,這個指揮中心不是自行啟動的,我們需要在web.xml中啟動它。這件事只需要做一次。 - 創建Action類
action負責處理用戶的請求,具體怎么處理,需要我們創建一個Action類來實現。 - 創建視圖
Action實現之后,我們要考慮返回怎樣的視圖給用戶。在這里,我們需要創建1到多個jsp文件,擔任視圖的角色。 - 配置action映射
配置name和class的對應關系,讓Struts知道該把哪些請求分派給哪個action。 - 配置result映射
Action返回的是一個普通的字符串,我們稱之為處理結果或者邏輯視圖,不管叫什么,總之它不是物理視圖,不指定任何視圖文件。Struts為了將Action類和視圖文件解耦,將返回結果和物理視圖的對應關系,我們稱之為result映射,在配置文件中配置。
上面五個工作,第一個只需要做一次,相對的,后面四個每創建一個action都需要做一次。
在上面五個工作中,視圖文件是jsp,我們將之視為基本知識,並不打算介紹。result映射一般是在action映射內部配置的,所以配置result將包含在配置actioin中。所以,我計划分下面三個主題,介紹上面的工作:
- 配置核心過濾器
- 創建Action
- 配置action
1.配置核心過濾器
這里使用Maven管理項目,如果要使用Struts框架,你需要引入依賴。你可以在http://mvnrepository.com/中輸入struts2-core來查找可用的版本,從中選擇一個並獲取其依賴配置:
<dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.28</version> </dependency>
在web.xml中配置下面的內容:
1 <filter> 2 <filter-name>struts2</filter-name> 3 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>struts2</filter-name> 7 <url-pattern>/*</url-pattern> 8 </filter-mapping>
2.創建Action
2.1.參數獲取
2.2.創建Action
- POJO
Action不需要實現任何接口,不需要繼承任何類,但是需要包含一個方法:public String execute() throws Exception。這個方法是處理請求的入口,由Struts框架調用。這個方法的名字可以自行定義,但是execute是默認的名字,使用這個可以省去一項屬性配置。 - 實現Action接口
全稱com.opensymphony.xwork2.Action,這個接口定義了幾個字符串常量,作為result;還定義了execute方法。 - 繼承ActionSupport
全稱com.opensymphony.xwork2.ActionSupport,實現了Action。
2.3.demo設計
為了介紹上面3種方式,這里設計一個demo。
2.3.1.POJO
1 package cn.ljl.note.struts2.login.actions; 2 3 public class LoginPOJO { 4 private static final String VALID_USER = "admin"; 5 private static final String VALID_PWD = "admin"; 6 7 private static final String SUCCESS = "success"; 8 private static final String LOGIN = "login"; 9 10 private String username; 11 private String password; 12 private String tip; 13 14 public String getUsername() { 15 return username; 16 } 17 public void setUsername(String username) { 18 this.username = username; 19 } 20 public String getPassword() { 21 return password; 22 } 23 public void setPassword(String password) { 24 this.password = password; 25 } 26 public String getTip() { 27 return tip; 28 } 29 public void setTip(String tip) { 30 this.tip = tip; 31 } 32 33 public String execute() throws Exception { 34 boolean validUser = VALID_USER.equals(getUsername()); 35 boolean validPwd = VALID_PWD.equals(getPassword()); 36 37 if (!validUser) { 38 setTip("用戶不存在!"); 39 return LOGIN; 40 } 41 42 if (!validPwd) { 43 setTip("密碼不正確!"); 44 return LOGIN; 45 } 46 47 setTip(null); 48 return SUCCESS; 49 } 50 }
2.3.2.實現Action接口
Action接口的類圖如下:
源代碼:

1 package cn.ljl.note.struts2.login.actions; 2 3 import com.opensymphony.xwork2.Action; 4 5 public class LoginAction implements Action{ 6 private static final String VALID_USER = "admin"; 7 private static final String VALID_PWD = "admin"; 8 9 private String username; 10 private String password; 11 private String tip; 12 13 public String getUsername() { 14 return username; 15 } 16 public void setUsername(String username) { 17 this.username = username; 18 } 19 public String getPassword() { 20 return password; 21 } 22 public void setPassword(String password) { 23 this.password = password; 24 } 25 public String getTip() { 26 return tip; 27 } 28 public void setTip(String tip) { 29 this.tip = tip; 30 } 31 32 @Override 33 public String execute() throws Exception { 34 boolean validUser = VALID_USER.equals(getUsername()); 35 boolean validPwd = VALID_PWD.equals(getPassword()); 36 37 if (!validUser) { 38 setTip("用戶不存在!"); 39 return LOGIN; 40 } 41 42 if (!validPwd) { 43 setTip("密碼不正確!"); 44 return LOGIN; 45 } 46 47 setTip(null); 48 return SUCCESS; 49 } 50 51 }
2.3.3.繼承ActionSupport類

1 package cn.ljl.note.struts2.login.actions; 2 3 import com.opensymphony.xwork2.ActionSupport; 4 5 public class LoginActionSupport extends ActionSupport { 6 7 private static final long serialVersionUID = 8451980703294866793L; 8 9 private static final String VALID_USER = "admin"; 10 private static final String VALID_PWD = "admin"; 11 12 private String username; 13 private String password; 14 private String tip; 15 16 public String getUsername() { 17 return username; 18 } 19 public void setUsername(String username) { 20 this.username = username; 21 } 22 public String getPassword() { 23 return password; 24 } 25 public void setPassword(String password) { 26 this.password = password; 27 } 28 public String getTip() { 29 return tip; 30 } 31 public void setTip(String tip) { 32 this.tip = tip; 33 } 34 35 @Override 36 public String execute() throws Exception { 37 boolean validUser = VALID_USER.equals(getUsername()); 38 boolean validPwd = VALID_PWD.equals(getPassword()); 39 40 if (!validUser) { 41 setTip("用戶不存在!"); 42 return LOGIN; 43 } 44 45 if (!validPwd) { 46 setTip("密碼不正確!"); 47 return LOGIN; 48 } 49 50 setTip(null); 51 return SUCCESS; 52 } 53 54 }
2.4.三種方式的比較
3.配置action
3.1.配置文件
1 <?xml version="1.0" encoding="GBK"?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 <struts> 6 7 </struts>
3.2.命名空間
一次請求的地址,像這個樣子:
<package name="login" extends="struts-default" namespace="/login"> ...... </package>
- name
name唯一標識一個package - extends
package有繼承的特性,使用extends指定另一個package的name,就會繼承彼package下所定義的內容。
這里繼承的struts-default是在struts2-core的struts-default.xml中定義的。通常,建議繼承struts-default。 - namespace
配置命名空間。
默認的命名空間
namespace不是必需的屬性,如果沒有配置,那就是默認的命名空間。默認的命名空間有特殊的作用:如果在請求的URL中解析出來的命名空間里找不到對應的action,就到默認的命名空間里找。默認命名空間是一個抽象的概念,不是默認值,你不能說“我可以通過配置namespace等於默認值,來指定默認命名空間”。比如"/"被稱為根命名空間,但是它不是默認命名空間,它也沒有任何特殊的性質,就和其他命名空間一樣。
3.3.action
即配置action映射,struts框架需要查找這個映射,才能根據URL找到實際的處理Action。action是在<package>元素內配置的,下面是一個demo:
<action name="check" class="cn.ljl.note.struts2.login.actions.LoginActionSupport" method="execute"> ...... </action>
- name
它同時也是action的url請求地址的一部分,同一個命名空間下,action的name要唯一 - class
它是Action類,負責處理用戶的請求。這是一個非必需的屬性,默認為com.opensymphony.xwork2.ActionSupport;你可以看下這個類的execute方法,只是直接返回SUCCESS。 - method
它是Action的方法名,對於框架來說,相當於回調方法。框架會調用這個方法,以達到通知請求到達的效果。這是一個非必需的屬性,默認值為execute,所以上面的demo完全不用配置這個屬性。
3.4.result
即配置result映射,根據這個映射,struts框架才能根據Action返回的邏輯結果(字符串)找到對應的視圖資源。result是在<action>元素內配置的,下面是一個demo:
<result name="success">/index.jsp</result> <result name="login">/login/login.jsp</result>
<result>元素的屬性name代表Action返回的邏輯結果;<result>體的內容,代表物理視圖的路徑。其中name的默認值是"success",所以第1行不用配置name屬性。
3.5.異常
1 public String execute() { 2 try { 3 // ... 4 } catch(異常1 e1) { 5 return 結果1; 6 } catch(異常2 e2) { 7 return 結果2; 8 } 9 }
然后我們會在struts.xml中這樣配置result映射:
<result name="結果1">視圖1.jsp</result> <result name="結果2">視圖2.jsp</result>
這樣是可以的,實際上在我們已知的知識上,想到這種方式來解決新的問題,能體現我們是會靈活變通的。不過Struts也提供了正統的配置方法,讓我們只需要配置異常和返回結果的映射關系,而不需要捕獲Action處理方法中拋出的異常。
3.5.1.異常映射
在<action>元素下,使用<exception-mapping>元素來配置,下面上一個demo:
<result name="exception">/exception/exception.jsp</result> <exception-mapping result="exception" exception="java.lang.Exception" />
- result
對應的異常類型發生時,要返回什么邏輯結果 - exception
配置什么類型的異常
- 配置的異常類型,對其子類型異常是否有效?
- 兩種異常類型(繼承關系)配置的先后順序,對異常拋出的返回結果是否有影響?
拋出的異常可以是父類型、子類型、兩者的子類型。
3.5.2.攔截器
我們雖然提供了異常到邏輯結果的映射,但是還需要一個攔截器來做這樣的工作:攔截拋出的異常,查映射關系,改為返回對應的邏輯結果。這樣的攔截器已經在struts-default中使用了,所以只需要保證定義的<package>,直接或間接的繼承了struts-default就好了
3.5.3.輸出異常
我們可以在jsp中使用el來輸出異常,像這樣${exception }。不過,Struts2也提供了相關的標簽,下面是一個demo:
1 <%@ page language="java" contentType="text/html; charset=GBK" 2 pageEncoding="GBK"%> 3 <%@ taglib uri="/struts-tags" prefix="s" %> 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 5 <html> 6 <body> 7 <s:property value="exceptionStack"/> 8 </body> 9 </html>
- exception
輸出異常本身,相當於${exception } - exception.message
輸出異常的信息,相當於${exception.message } - exceptionStack
輸出異常的堆棧信息,正是demo中所用到的。
3.6.總結
好了,讓之前出現過的幾位也上來吧,我們來張合照:struts.xml
1 <?xml version="1.0" encoding="GBK"?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 <struts> 6 <package name="login" extends="struts-default" namespace="/login"> 7 <action name="check" class="cn.ljl.note.struts2.login.actions.LoginActionSupport"> 8 <result name="success">/index.jsp</result> 9 <result name="login">/login/login.jsp</result> 10 <result name="exception">/exception/exception.jsp</result> 11 <exception-mapping result="exception" exception="java.lang.Exception" /> 12 </action> 13 </package> 14 </struts>
4.使用config-browser查看配置
你可能想瀏覽配置的情況,當然對於一個簡單的項目,直接看struts.xml可能更快、更專業。接下來要出場的是一個插件,config-browser,它可以提供通過前台查看配置情況的功能。這不是它唯一的優點,但是我們不需要為其優點列一個清單,暫時知道這一個吧,剩下的自己體會。
4.1.添加依賴
我們使用的struts2-core是2.3.28版本,我們也使用相同版本的config-browser:
<dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-config-browser-plugin</artifactId> <version>2.3.28</version> </dependency>
4.2.使用
重新打包、部署,重啟服務,輸入訪問地址:http://localhost:8080/note-struts2/config-browser/actionNames.action,可以看到下面的內容:
注意左側導航欄,Namespaces下是所有的命名空間,其中/config-browser是插件定義的,/login是我們之前定義的。右側默認顯示默認命名空間下的action。
點擊左側Namespaces/login鏈接,可以看到:
右側列出了這個命名空間下的action,點進去會看到:
注意:這里的內容是按照標簽頁組織的,默認顯示的是Results標簽頁,切換到Eception Mappings,可以看到:
我們關於config-browser入門的介紹就到這里了,其他的靠大家自己了。
5.擴展
前面已經對框架的基本流程中涉及到的工作,做了基本的介紹,下面這些內容會深入一下。
5.1.action
5.1.1.多個處理方法
一個Action類中可以有多個處理方法,只需要在配置<action>的時候使用method指定不同的方法名,就可以定義多個action。比如,我們假想一個Action,它的類圖是這樣的:
我們可以在struts.xml中這樣配置:
1 <package name="imagination" extends="struts-default" namespace="/imagination"> 2 <action name="addUser" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="add"> 3 ... 4 </action> 5 <action name="saveUser" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="save"> 6 ... 7 </action> 8 <action name="deleteUser" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="delete"> 9 ... 10 </action> 11 </package>
5.1.2.使用通配符
在滿足一定的模式的情況下,使用通配符可以使用最少的配置量,配置多個action。比如對於上面的情況,也可以使用下面的配置:
1 <package name="imagination" extends="struts-default" namespace="/imagination"> 2 <action name="*User" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="{1}"> 3 ... 4 </action> 5 </package>
line 2,name屬性值中使用了*,method屬性值中的{1}表示使用第1個*所匹配的字符串。這個配置和上文的配置效果是一樣的,但是配置工作更少。
class也可以和name滿足一定的模式,比如下面的配置:
1 <package name="imagination" extends="struts-default" namespace="/imagination"> 2 <action name="*User" class="cn.ljl.note.struts.imaginary.actions.{1}UserAction"> 3 ... 4 </action> 5 </package>
- 完全匹配優先於模式匹配
- 模式匹配中,前面的優先
5.1.3.默認的action
*可以匹配一切,所以我們可以使用*來配置默認的action。另外,我們還可以像下面這樣配置:
1 <package name="imagination" extends="struts-default" namespace="/imagination"> 2 <default-action-ref name="default" /> 3 <action name="default" class="..."> 4 ... 5 </action> 6 ... 7 </package>
在特定命名空間下配置,只能作為當前命名空間的默認action;在默認的命名空間下配置,可以作為全局的默認action。
5.1.4.默認的class
配置<action>時,默認的class是com.opensymphony.xwork2.ActionSupport,你可以修改這項配置:在<package>下添加<default-class-ref class="" />。
5.1.5.動態方法調用
在form標簽的action屬性,可以同時指定action的name和方法,比如:
<form action="user!add"> ... </form>
就指定name為user的action的add方法,來處理請求。
5.2.result
Struts2支持多種result-type,基本的result-type都是在struts2-core的struts-default.xml中配置的。
5.2.1.多種結果類型
1.dispatcher
disparcher是默認的result-type,以指定的jsp作為視圖。最原始的配置方式是這樣的:
<result name="success" type="dispatcher"> <param name="location">/success.jsp</param> </result>
因為result的name默認就是success,type默認就是dispatcher,而視圖的location可以直接在<result>的體配置。所以可以簡化成這個樣子:
<result>/success.jsp</result>
2.plainText
plainText將指定的視圖以文本的形式顯示給瀏覽器。使用這種方式,需要指定視圖的location;如果視圖文件中包含非西歐字符,還要指定charSet。
<result name="success" type="plainText"> <param name="location">/success.jsp</param> <param name="charSet">GBK</param> </result>
3.redirect
重定向。這個類型可以指定一個location,瀏覽器重定向到指定的視圖。
<result name="success" type="redirect">/index.jsp</result>
4.redirectAction
重定向到Action。這個類型專門重定向到Action,與redirect算是被包含關系。這個類型可以指定namespace和actionName:
<result type="redirectAction"> <param name="namespace">/login</param> <param name="actionName">check</param> </result>
暫時,就介紹這幾種吧。
5.2.2.使用通配符
在配置result映射的時候,也可以使用通配符,比如:
<package name="imagination" extends="struts-default" namespace="/imagination"> <action name="*User" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="{1}"> <result>/imagination/result-{1}.jsp</result> </action> </package>
按照上面的配置,addUser對應cn.ljl.note.struts.imaginary.actions.UserAction的add方法,返回的視圖是/imagination/result-add.jsp。
5.2.3.使用OGNL表達式
OGNL表達式的具體內容,計划放在后面講,這里只介紹幾個簡單的用法。配置result映射時,可以使用action的屬性值(要提供getter方法),比如:
<result>/imagination/result-add.jsp?username=${username}</result>
在計算物理視圖時,就會使用action的username屬性,替換其中的${username}。
如果屬性是復雜屬性,比如bean,而在result中需要的是屬性bean的屬性,也可以按照這樣的方式獲取:${user.name}。
5.2.4.全局配置
在之前的配置里,都是針對action的一個邏輯結果進行配置;全局配置是在<package>范圍,提供一個配置,對所有action都有效。
全局配置是在<package>下,使用<global-results> - <result>來配置,比如:
1 <package ...> 2 <global-results> 3 <result name="exception">/exception.jsp</result> 4 </global-results> 5 ... 6 </package>
這樣一來,這個package下所有的action,如果沒有指定"exception"的映射,就使用全局的映射;如果指定了,就覆蓋全局的配置。
5.3.異常的全局配置
在<package>范圍,異常也可以使用全局配置。使用<global-exception-mappings> - <exception-mapping>。異常映射是把異常的類型映射到result,所以它依賴於result,全局的異常映射應該只使用全局的result,像下面這樣:
1 <package ...> 2 <global-results> 3 <result name="exception">/exception.jsp</result> 4 </global-results> 5 <global-exception-mappings> 6 <exception-mapping exception="java.lang.Exception" result="exception" /> 7 </global-exception-mappings> 8 ... 9 </package>