背景:Struts1是一個高度成熟的框架,運行效率高,但其致命缺陷在於與JSP/Servlet的耦合很是緊密,於是致使了一些嚴重問題。其次,Struts1與Servlet API的嚴重耦合,使應用難以測試;Struts1代碼嚴重依賴Struts1 API,屬於侵入式框架。因爲其種種侷限性,纔有了Struts2的誕生。html
Struts2與1差異巨大,Struts2以WebWork爲核心,採用攔截器的機制處理用戶請求,這樣的設計使業務邏輯控制器與Servlet API徹底脫離開。java
Part 1 Struts2快速入門web
1. 安裝與配置apache
下載struts-2.3.15.1-all.zip,這裏採用該版本,打開後有文件夾:apps 實例代碼 ,docs 幫助文檔,lib jar包,src 源代碼文件;瀏覽器
2.開始寫項目tomcat
1.添加jar包;(可直接複製實例項目,複製jar)安全
2.在web.xml中加入struts2的 filter ,內容以下:服務器
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9",version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www/w3/org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html<welcome-file> </welcome-file-list> </web-app>
從配置文件中,能夠看出, Struts2 其實就是一個filtersession
3.在src文件夾下添加struts.xml配置文件架構
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="default" namespace="/" extends="struts-default"> <action name="index"> <result>/index.jsp</result> </action> </package> </struts>
4. 寫jsp頁面,或html頁面;寫java bean, action, dbutils(此實例爲簡明,省略了這些內容);
最簡單的示例,在web文件夾下寫 index.jsp
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"> <% String path=request.getContextPath(); String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> </head> <body> First One </body> </html>
測試:部署tomcat或直接使用preview server,http://localhost:port/項目名/index.adtion
注意, .action
Part 2 Struts2工做流程和核心概念
Struts2使用了WebWork的設計核心(XWork),在內部使用攔截器處理用戶請求,從而容許用戶業務邏輯控制器和ServletAPI分離。Struts2內部是一個MVC架構,Struts2 的核心控制器是FilterDispatcher,客戶端發送請求,而通過核心控制器FilterDispatcher處理,根據頁面發送的請求,從而肯定請求的是哪一個action,而action是MVC中的Model,最後肯定返回哪一個頁面(html或jsp)
具體工做流程:
(1)瀏覽器發送請求;
(2)核心攔截器FilterDispatcher根據請求決定調用合適的action;
(3)攔截器自動對應運用通用功能;
(4)回調用action上的execute()方法;
(5)action的execute()方法處理信息結果輸出到瀏覽器。
2)核心概念:當瀏覽器發送請求到Servlet容器時,會經歷一系列Filter過濾器,這些過濾器包括ActionContextCleanUp,接着FilterDispatcher被調用,經過參考ActionMapper決定一個請求和一個Action關聯。FilterDispatcher參考框架的配置文件管理,ActionProxy生成一個ActionInvocation,它負責執行命令,並負責查找適當的結果以和struts.xml中的action result相比較,調用一個在JSP或FreeMarker中繪製的模板。而Action是須要與struts.xml配置文件一塊兒結合使用的,下面將詳細講述配置文件使用:
[1] struts.xml文件配置
先來個實例:
login.jsp
<%@page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path=request.getContextPath(); String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title></title> </head> <body> <form action="login" method="post"> 用戶名:<input type="text" name="uname"></br> 密碼:<input type="password" name="pwd"></br> <input type="submit" value="登陸"> </form> </body> </heml>
LoginAction類
public class LoginAction{ private String uname; private String pwd; public String getUname(){ return uname; } public void setUname(String uname){ this.uname=uname; } public String getPwd(){ return pwd; } public void setPwd(String pwd){ this.pwd=pwd; } public String excute(){ if("JokerJason".equals(uname)&&"12345".equals(pwd)){ return "success"; } return "fail"; } }
注意:在上述LoginAction中,屬性uname和pwd必須和頁面保持一致,這是由於在Struts2中提供屬性參數及set/get方法經過ioc的方式將頁面參數注入到Action類裏。
寫完Action類,對其進行配置,在src目錄下建立struts.xml文件,以下
<struts> <package name="default" namespace="/" extends="struts-default"> <action name="login" class="LoginAction"> <result name="success">success.jsp</result> <result name="fail">fail.jsp</result> </action> </package> </struts>
(1)package: 包在java中是用來分類的,在Struts2中,也是用來分類的,在Struts2的配置文件中,一個文件能夠有多個包;
package有不少屬性,name是包名,namespace是命名空間,若是訪問一個Action,則必定要是namespace+actionname;
如本例中,要訪問LoginAction,則必須是:http://localhost:8080/項目名/login;最後的login就是namespace+action;
extends指定繼承哪一個Struts的組件,一般都是繼承struts-default才能使用Struts中組件。固然若是想使用JSON,就須要繼承JSON-default;
(2)action:action是核心控制器FilterDispatcher將要訪問的具體action,name指定的是action的名稱,class指定的是具體類名,一般是包名+類名;
(3)result:指的是返回的結果頁面,name指定返回結果,在實際路徑頁面中必定要加"/"
[2]Action對象詳解
Struts2中Action無須實現任何接口和任何類型,可是爲了方便實現Action,大多數狀況下采用下面兩種方式對Action進行處理:
(1)繼承com.opensymphony.xwork2.ActionSupport;
(2)實現com.opensymphony.xwork.Action;
默認狀況下,都須要重載(Override)或者覆寫(Overload)其中的String excute()throws exception方法。
下面對上面實例進行修改,兩個環節進行:
1.將LoginAction繼承ActionSupport
import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport{ private String uname; private String pwd; public String getUname(){ return uname; } public void setUname(String uname){ this.uname=uname; } public String getPwd(){ return pwd; } public void setPwd(String pwd){ this.pwd=pwd; } public String execute(){ if("jokerjsaon"equals(uname)&&"1234"equals(pwd)){ return "fail";//與struts.xml中配置的result相同 } } }
2.在struts.xml中進行配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache SoftWare Foundation//DTD Struts Configuration 2.3//EN " "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="default" namespace="/" extends="struts-default"> <action name="login" class="LoginAction"> <result name="success">success.jsp</result> <result name="fail">fail.jsp</result> </action> </package> </struts>
這樣修改並未看到太大變化,反而會由於繼承一個ActionSupport類增長了累贅,但,先看一下Action接口的實現方式:
package com.opensymphony.xwork2; public interface Action{ public static final String SUCCESS="success"; public static final String NONE="none"; public static final String ERROR="error"; public static final String INPUT="input"; public static final String LOGIN="login"; public String execute()throws Exception; }
表面上看實現Action接口沒有什麼好處,事實上實現改接口,有利於更好的實現Action類。Action接口中定義了五個字符串常量,由於ActionSupport是Action的實現類,因此繼承了ActionSupport也就可使用這五個常量了:以下
import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport{ private String uname; private String pwd; public String getUname(){ return uname; } public void setUname(String uname){ this.uname=uname; } public String getPwd(){ return pwd; } public void setPwd(String pwd){ this.pwd=pwd; } public String execute(){ if("jokerjsaon"equals(uname)&&"1234"equals(pwd)){ return ERROR;//注意此處 } } }
Part 3 Struts2配置文件
1. Struts的配置文件類型
Struts2的配置文件分爲內部使用和供開發人員使用兩大類,內部配置文件由Struts2框架自動加載,對其自身進行配置,其餘的配置文件由開發人員使用,用於對Web應用進行配置。Struts2所需的配置文件以下表:
文件 | 位置 | 用途 |
web.xml | /WEB-INF/ | Web部署描述文件 |
struts.xml | /WEB-INF/classes | 包含result映射,action映射,攔截器配置等 |
struts.properties | /WEB-INF/classses | 與struts.xml做用同樣,不一樣的配置文件罷了 |
struts-default.xml | /WEB-INF/lib/struts-core.jar | Struts2提供的默認配置,由框架提供 |
struts-plugin.xml | /WEB-INF/lib/struts-xxx-plugin.jar | Struts2框架插件所提供的配置文件 |
下面對核心內容進行解釋:
[1] struts.xml配置文件:是該框架的核心配置文件,用於配置和管理開發人員編寫的Action,在這個配置文件中,開發人員能夠配置做用於Action的攔截器,Action和result的映射等。
[2]struts-default.xml: 基礎配置文件,爲框架提供默認設置,該文件包含在struts2-core-2.x.jar中,由框架自動加載。其會被自動包含在struts.xml中,以提供標準的配置設置而不須要複製其內容:
<package name="default" extends="struts-default"/>
(其內容包括Servlet轉發,Servlet重定向,FreeMarker模板輸出,XSTL渲染,內置攔截器和不一樣攔截器組成的攔截器棧等等)
2. Struts2的包配置
Struts2中的包相似於java中的包,提供了將action,result,攔截器和攔截器棧組織爲一個邏輯單元的一種方式。Struts2中的包能夠擴展另外的包,從而繼承原有包的全部定義,並能夠添加本身包的特有配置,以及修改原有包的部分配置。在struts.xml中庸package定義包,下面爲屬性:
+ name:(必須) 被其餘包引用的key(鍵);
+ extends: (沒必要須) 指定須要擴展的包(若是須要擴展多個包,用","逗號隔開,父包必須在子包前面) ;
+ namespace:(沒必要須) 指定命名空間;
+ abstract :(沒必要須) 聲明包爲抽象(這樣包內不能再有action的定義,好比前面struts-default.xml就是抽象包,可使用抽象包進行一些默認配置,其餘包繼承以減小工做量);
實例:
<package name="default" extends="struts-default"> <action name="login" class="LoginAction"> <result name="success">/success.jsp</result> <result name="error">/error.jsp</result> </action> </package> <package name="test" extends="default" namespace="/my"> </package>
當發起/my/login.action的路徑請求時,會先在test包內查找是否有合適的action,而在此時是在test包中存在LoginAction處理請求,由於test包繼承了default包,某種意義上包的extends至關於java的extends。
3. 名稱空間配置
package的namespace屬性能夠將包中的action配置爲不一樣的名稱空間,這樣在不一樣名稱空間中可使用同名action。Struts2框架使用action名字和它所在的名稱空間來標識一個action。
當Struts2接收到一個請求時,它會將請求URL分爲namespace和action名字兩個部分,而後Struts2從struts.xml中查詢namespace/action這個命名對,若是沒有找到,就會在默認的名稱空間搜索相應的action名。默認名稱空間用空字符串" "表示,若是在定義包時沒有使用namespace,就指定了默認名稱空間。
此外,Struts2還支持以"/"命名的根名稱空間,若是直接請求Web應用上下文路徑下的action,那麼框架會在根名稱空間查找相應的action,若是沒有找到就在默認名稱空間查找:
<!--default包在默認名稱空間中--> <package name="default" extends="struts-default"> <action name="foo" class="LoginAction"> <rfesult name="success">foo.jsp</result> </action> <action name="bar" class="LoginAction"></action> <!--mypackage1包在根名稱空間裏--> <package name="mypackage1" namespace="/"> <action name="moo" class="LoginActionTwo"> <result name="success">/moo.jsp</result> </action> </package> <!--mypackage2包在/accp名稱空間中--> <package name="mypackage2" namespace="/accp"> <action name="foo" class="LoginActionThree"> <result name="success">/foo2.jsp</result> </action> </package>
當發起請求/moo.action時,框架會在根空間('/')查找moo.action,若是沒有找到再在默認空間查找,此例中Mypackage中存在moo.action,所以它會被執行,結果轉向moo.jsp。
4. Action相關配置
struts2的核心功能是Action,對於開發人員來講,使用struts2框架主要的工做就是編寫action類。Action類通常都要實現Action接口,並實現接口中的execute()方法:簽名以下:
public String execute()
Struts2並不要求本身編寫的Action類必定實現Action接口,也能夠編寫普通java類做爲Action,只要此類提供一個返回類型爲String的無參public方法:
public String xxx()
實際開發中,Action類不多使用Action接口,一般都是繼承ActionSupport類。
開發好Action後,在struts.xml配置,以告訴Struts2框架,針對某個URL的請求應該交給哪一個Action處理。
struts.xml中action元素屬性以下:
+ name 必須 Action名稱,用於匹配請求;
+ class 非必須 Action實現類的完整類名
+ method 非必須 執行Action調用的方法;
+ converter 非必需 應用於Action的類型轉換器的完整類名;
! 注意 : 在爲Action取名時,默認狀況下不準出現斜槓("/"),若是必須使用,例如<action name="book/manage" class="LoginAction">,就須要在struts.xml中加入: <constant name="struts.enable.SlashesInActionNames" values="true">
此外,不在取名中使用"_"和"."避免一些莫名其妙的問題!
前面講述了在Struts2中可使用一個普通的java類做爲Action類,並且在此類中的方法沒有特殊要求,對於此點Struts2如何處理?其實Action中有一個method屬性能夠自定義方法,而不須要使用默認的excute()方法。
例如在一個新聞發佈系統中,對新聞有四種操做,添加,修改,刪除,查詢。在具體實現中,爲了節約Action數量,一般在一個Action中編寫四個方法實現CURD操做:
public class CurdAction(){ public String addNews(){return SUCCESS;} public String deleteNews(){return SUCCESS;} public String updateNews(){return SUCCESS;} public String selectNews(){return SUCCESS;} }
如今的問題是,如何才能讓框架在不一樣請求到達時,去調用CurdAction裏面的相應方法呢?在執行Action時,默認的調用方法是execute()方法。Action中的method屬性能夠指定不一樣的方法,以下配置:
<package name="default" extends="struts-default"> <!--請求/list時,調用CurdAction類上的selectNews方法--> <action name="list" class="CurdAction" method="selectNews"> <result>/list.jsp</result> </action> <action name="edit" class="CurdAction" method="editNews"> <result>/edit.jsp</result> </action> <action name="delete" class="CurdAction" method="deleteNews"> <result>/delete.jsp</result> </action> <action name="other" class="CurdAction"> <result>/edit.jsp</result> </action> </package>
Struts2在根據Action元素的method屬性查找方法時有兩種途徑:
+ 查找與method屬性值徹底一致的方法;
+ 查找doMethod()形式的方法;
在上例中,當請求/list時,Struts2首先查找selectNews()方法,若是找不到,則繼續查找名爲doCreate()的方法;
[另一種無須配置就能夠直接調用Action中的非execute()方法的方式,就是直接使用struts2動態方法DMI,語法以下
actionName!methodName.action
例如:
<action name="list" class="action.CurdAction"> ... </action>
當發起/list!selectNews.action時,將調用CurdAction類中的selectNews方法或doSelectNews().
注意:DIM存在安全隱患,能夠在struts.xml中配置禁止使用:
<constants name="struts.enable.DynamicMethodInvocation" value="false"/>
]
5.通配符簡化配置
一般來講,應用越大,action配置數量越多,經過使用通配符,能夠將一些類似的mapping綁在一塊兒,用一個通用的mapping表示。
優點:大大減小配置文件內容;
劣勢:可讀性差;
使用原則:約定高於配置;在項目中,不少命名規則是約定的,使用通配符必須有一個約定,以下
<action name="User_*" class="action.UserAction method="{1}""> <result>/{1}success.jsp</result> </action>
*表示匹配全部,在struts2中,{1}表示第一個*號。
此時若是UserAction裏面有100個方法,只須要配置一次便可;可是若是有多種Action類,就須要以下修改:
<action name="*_*" class="action.{1}Action" method="{2}"> <result>/{1}_{2}_success.jsp</result> </action>
注意:在開發前,約定優先於配置,大大下降工做量。
6.返回結果的配置
一個result表明了一個可能的輸出。當Action類的方法執行完成後,返回一個字符串類型的結果碼,框架根據這個結果選擇對應的result向用戶輸出。
result配置由兩部分組成:(位於struts.xml中)
+ result映射
+ result類型
result的屬性以下:
屬性 | 必要性 | 說明 |
name | 否 | 指定result的邏輯名 |
type | 否 | 指定result的類型,不一樣類型的result表明了不一樣類型的輸出結果 |
具體配置方法有三種:
+ 最複雜的配置:
<package name="default" namespace="/" extends="struts-default"> <action name="login" class="action.LoginAction"> <result name="success" type="dispatcher"> <param name="location">/success.jsp</param> </result> </action> </package>
+ 精簡版
<package name="default" namespace="/" extends="struts-default"> <action name="login" class="action.LoginAction"> <result name="success"> <param name="location">/success.jsp</param> </result> </action> </package> <!--這裏沒有設定type屬性,由於默認爲dispatcher-->
+簡化版
<package name="default" namespace="/" extends="struts-default"> <action name="login" class="action.LoginAction"> <result name="success">/success.jsp </result> </action> </package>
上述的dispatcher是什麼?下面看struts2-core.jar包下面的struts-default.xml,發現以下配置
<result-types> <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionResult"/> <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult"/> </result-types>
這些配置就表明了struts2中全部的result配置類型.
Struts2框架調用Action對請求處理後,就要向用戶呈現一個結果視圖。Struts2支持多種類型的視圖,由不一樣的結果類型來管理。一個結果類型就是實現了com.opensymphony.xwork2.Result接口的類。
Strut2中預約義了多種結果類型,以下圖:
結果類型 | 說明 |
chain | 用於Action鏈式處理 |
dispatcher | 用於web資源的集成,包括JSP的集成 |
freemarker | 用於FreeMarker的集成 |
httpheader | 用於控制特殊的HTTP行爲 |
redirect | 用於重定向到另外的URL(web資源) |
redirectAction | 用於重定向到另外的action映射 |
stream | 用於向瀏覽器返回一個InputStream(用於文件下載) |
velocity | 用於velocity集成 |
xslt | 英語XML/XSLTWYS DN |
plaintext | 用於顯示某個特定頁面(如jsp,html的原始內容) |
下面對常見的result配置類型詳細介紹:
(1)dispatcher結果類型
最多見的結果類型主要是dispatcher類型,此結果類型也是要struts2中默認的結果類型。Struts2在後臺使用Servlet API的RequestDispatcher來轉發請求,所以在用戶的整個請求過程當中,目標Servlet API接收的request/response對象始終是同一個。Dispatcher結果類型實現類爲ServletDispatcherResult,該類有兩個屬性:location和param.這兩個屬性經過struts.xml中的result元素的param子元素設置。以下配置:
<result name="success" type="dispatcher"> <param name="location">/success.jsp</param> <param name="parse">true</param> </result>
location參數用於指定action執行完畢後要轉向的目標資源,location是默認的參數,能夠不用給出.
parse用於指定是否可以解析location參數中的OGNL表達式。若是爲false則不解析,默認爲true.以下:
實例:一個新聞系統,每一個新聞一個id,當用戶點擊新聞連接,一個帶id參數的請求發送到Action類裏,Action處理完後轉跳到view.jsp
<action name="view" class="ViewAction"> <result name="success" type="dispatcher"> <param name="location">/view.jsp?id=${id}</param> <param name="parse">true</param> </result> </action>
(2) redirect結果類型
在一次用戶交互中,存在兩次請求,一次是客戶端發送給服務器,服務器sendRedirect(),而後客戶端第二次訪問redirect後的目標資源。這就意味着在目標資源中不能訪問Action實例,解決方案以下: + 數據保存在session裏;
+ 經過請求參數來傳遞;
如:用戶登陸頁面裏,登錄成功後使用redirect結果類型將其重定向到歡迎頁面,在歡迎頁面中顯示出來。採起第二種方法:
<action name="login" class="LoginAction"> <resulttype="redirect">x.jsp?username=${username}</result> </action>
若是LoginAction裏username屬性值爲JokerJason,則重定向後瀏覽器中顯示的URL地址爲hhtp://localhost:8080/..../x.jsp?username=JokerJason
(注意:加密處理)
(3) redirectAction結果類型
redirectAction與redirect的後臺原理是同樣的,都是利用HttpServletResponse的sendRedirect方法將請求重定向到指定的URL,那麼二者的區別呢?
(正在更新)