Struts2官網:http://struts.apache.org/html
目前最新版本:Struts 2.3.24前端
Struts1已經徹底被淘汰了,而Struts2是借鑑了webwork的設計理念而設計的基於MVC的框架。感興趣的能夠了解一下webwork概念,這裏不作涉及。咱們常說的Struts2 Spring Hibernate三大面試中常被問到的框架,其實Struts2和SpringMVC是兩個獨立的MVC框架。並且SpringMVC是目前最好的MVC框架(我的感受),可是因爲舊的一些項目運維須要Struts2框架的知識,故而作整理。web
下載了Struts2框架以後,咱們要學習什麼?1.學習框架運做的整個流程;2.學習如何搭建框架;3.本身動手寫一個。先來看看框架的流程,以下:面試
Struts2(因爲Struts1表現層單一,沒法跟Freemarker等技術整合),它採用攔截器的機制來處理用戶的請求。apache
先來說講Struts2的原理圖,如上圖所示:後端
1.當用戶發起請求時(一個URL),服務器端的Web容器收到了請求。api
2.這時,Struts2的核心控制器FilterDispatcher接受用戶發起的請求,而後判斷這個請求是交給action處理?仍是交給web組件來處理?若是請求的action或web組件不存在,則報404錯誤。在整個處理過程當中,須要一個輔助對象:Action映射器(ActionMapper),ActionMapper會肯定調用哪一個Action(這個過程的實現是依靠ActionMapper返回一個收集Action詳細信息的ActionMaping對象)緩存
3.而後,來交給Action來處理,它會根據struts.xml的配置信息(首先執行攔截此action的全部攔截器,而後再執行請求的action對象<在這個處理過程當中須要輔助對象:Action代理(ActionProxy);配置管理器(ConfigurationManager);ActionInvocation>,),服務器
4.Action執行完畢以後,返回一個結果(此結果用字符串來表示),這個結果通過攔截Action的全部攔截器以後,返回給主控制器。主控制器根據此結果從配置文件中找到真正的路徑,而後將請求轉發給相應的視圖。app
5.由視圖客戶端做出響應。
那麼接下來咱們講講搭建框架,最好本身親自搭建一遍或者跟着舊的項目把流程走一遍:
首先,將下載好的Struts2中的jar包拷貝到你構建的web project中,根據上圖的設計流程,咱們知道Struts2是經過過濾器,將全部的請求過濾,而後分發到各個action(action其實就是一個類,就是一個POJO類),而後根據返回的String字符串,查找Struts2的配置文件,找到應該返回到哪一個頁面,便可。具體以下:
拷貝jar包到項目中,
再來配置web.xml:
<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>
再來配置struts.xml配置文件(struts.xml要在src目錄下)
<struts> <package name="default" extends="struts-default" namespace="/"> </package> </struts>
再來建立Action(它就是一個POJO類)
public class HelloAction{ public String execute(){ System.out.println("Hello Struts2!");
return "success"; } }
再在struts.xml中配置action和返回結果集:
<struts> <package name="default" extends="struts-default" namespace="/"> <action name="hello" class="com.hp.it.HelloAction">
<result name="success">/hello.jsp</result>
</action> </package> </struts>
注意,這裏的<action>標籤中的name屬性要與url路徑中的過濾到的string相對應;class屬性要與你寫的action類相對應(注意要有包名).<result>標籤(result是結果集)中的那麼屬性要與action中返回的string字符串相對應,返回的路徑是WEB-INF/下的hello.jsp,即http://localhost:8080/projectname/hello.action
再寫前臺的hello.jsp:
<html> <head></head> <body><h1>Hello</h1></body> </html>
整個過程就是這樣的,那麼問題來了:當有多個請求的時候,要寫不少execute()方法麼?excute方法是默認的哦!
固然不是的,咱們注意到,在struts.xml的配置文件中,標籤<action>中除了name class 屬性,還有一個method屬性,很顯然,這個標籤能夠指定咱們映射到的方法。這個固然就是幾個簡單的擴張了。相應的struts.xml的配置文件:
<package name="default" extends="struts-default" namespace="/"> <action name="addUser" class="com.hp.it.UserAction" method="addUser"> <result name="success">/WEB-INF/user/addUser.jsp</result> </action>
<action name="updateUser" class="com.hp.it.UserAction" method="updateUser">
<result name="success">/WEB-INF/user/updateUser.jsp</result>
</action>
</package>
相應的UserAction中以下:
public class UserAction{ public String addUser(){ System.out.println("addUser"); return "success"; } public String updateUser(){ System.out.println("updateUser"); return "success"; } }
那麼問題又來了,一個方法寫一個action,那麼這樣會致使配置文件中的action量很大。一種解決辦法是能夠在<package>標籤的平行目錄下,增長標籤以下所示:經過增長<include>標籤來導入其餘的xml文件。
<struts> <package name="default" extends="struts-default" namespace="/"> <action name="hello" class="com.hp.it.HelloAction"> <result name="success">/hello.jsp</result> </action> </package> <include file="otherStruts.xml"> </struts>
引入其餘的xml文件,當然能夠,可是依然沒法解決action配置文件過多的問題,在整理Struts2提供了兩種解決方案,以下:
第一種URL:
http://localhost:8080/projectname/User!add http://localhost:8080/projectname/User!update http://localhost:8080/projectname/User!list
如上三個URL所示,User是Action類名,對應UserAction類:後面的!+方法名。
第二種URL:
http://localhost:8080/projectname/User?method:add http://localhost:8080/projectname/User?method:update http://localhost:8080/projectname/User?method:list
如上三個URL所示,User是Action類名,對應UserAction類:後面的?+method:方法名。
再接着,咱們看看咱們的struts.xml該如何來寫?
<package name="default" extends="struts-default" namespace="/"> <action name="user" class="com.hp.it.UserAction" > <result name="add">/WEB-INF/user/addUser.jsp</result> </action> <action name="user" class="com.hp.it.UserAction" > <result name="update">/WEB-INF/user/updateUser.jsp</result> </action> <action name="user" class="com.hp.it.UserAction" > <result name="list">/WEB-INF/user/listUser.jsp</result> </action> </package>
注意了啊,這裏的action屬性name是url中那個user,便是對應着UserAction.方法是由調用的時候來決定的看具體使用誰。
一樣的,咱們來看看在UserAction中應該這樣來寫:
public class UserAction{ public String addUser(){ System.out.println("addUser"); return "add"; } public String updateUser(){ System.out.println("updateUser"); return "update"; } public String listUser(){ System.out.println("listUser"); return "list"; } }
這個方法雖然減小了action的配置,可是增長了大量的結果集的配置。全部問題來了,有沒有解決這個問題的方法呢?有
咱們能夠經過通配符來解決這個問題,這兒有一個核心思想:(約定優於配置),以下:
<action name="*_*" class="com.hp.it.action.{1}Action" method="{2}"> <result>/WEB-INF/{1}/{2}.jsp</result> </action>
這裏要強調一下,標籤<result>默認的屬性是 name="success"。約定優於配置,那麼咱們的約定是對於URl來講,它的格式應該以下面這樣來向服務器端發出請求:
http://localhost:8080/projectname/User_add http://localhost:8080/projectname/User_update http://localhost:8080/projectname/User_list
上面這種對於URL的約定,直接可使用通配符*_*來對它過濾。大大簡化了配置文件的大小。(這種狀況下,注意大小寫字母)
前面這些都是在說,服務器端的跳轉,那麼客戶端的跳轉怎麼來實現呢?好比說,咱們的User類在add完成以後,每每要跳轉到它的list頁面,這時候應該這樣來配置:
<action name="*_*" class="com.hp.it.action.{1}Action" method="{2}">
<result>/WEB-INF/{1}/{2}.jsp</result>
<result type="redirect" name="r_list">/{1}_list.action</result>
</action>
大概看這個的含義就是說,當name=r_list的時候,進行重定向,而且重定向到{1}_list.action。相應的UserAction應該這麼寫
public class UserAction{ public String addUser(){ System.out.println("addUser"); return "r_list"; } public String updateUser(){ System.out.println("updateUser"); return "update"; } public String listUser(){ System.out.println("listUser"); return "list"; } }
若是按上述方法來作,是否是效果會更好呢。可是咱們一般看到的URL,每每不多再其屁股後面加".action"這個後綴,其實這個是能夠
上面這個配置語句配置了對於.action的請求都進行過濾,一樣也能夠咱們本身設定,以下:
<constant name="struts.action.extension" value="action,do,zxg" />
如上這種,當以.action;.do;.zxg的URL路徑訪問的時候,都會進入filter來過濾的。
*************************************************************************************************************************
接下來,咱們再看看Struts2中是如何對參數傳值作處理的(瞭解地址和類的對應關係;瞭解數據的通訊(參數)的)。這部分是很關鍵的,並且必定要掌握清楚,不要跟SprigMVC相混淆。這段邏輯若是錯誤的話,調試代碼的時候,介於前端和後端之間,斷點加了也進不去,很是很差調控,因此在掌握原理的時候必需要掌握的清清楚楚的。那麼接下來說講struts2傳遞參數的三種方案,分別以下:首先給你一個URL
http://16.158.70.172:8080/wstax-admin/report/assetsTransactionsByRegionTime?startTime=2015-06-01&endTime=2015-06-10&_=1434004102399
如上圖,分析這個URL以下,前面的16.158.70.172是IP地址,至關於localhost,至關於127.0.0.1.而後是項目名稱wstax-admin,而後是路徑名稱,而後咱們看這個action name="assetsTransactionsByRegionTime"其後傳過來三個參數,startTime和endTime分別是起始和截止時間,而後是後面的_1434004102399這個字符串,這是因爲get請求的時候,加一個由時間隨機生成的字符串,這樣保證了每一個url不一樣,這樣每次就不會再去取緩存中的東西,而是去服務器上的東西,保證每次取的資源都是更新事後最新的資源。如今咱們攔截了這個請求,傳參的方法是在Action中,定義一個跟參數徹底相同名字的變量,寫getter和setter方法。以下:
@Controller("dailyMonitoringAction") @Scope("prototype") public class DailyMonitoringAction extends BaseAction { private static final long serialVersionUID = -2065341145635610669L; @Autowired private IDailyMonitoringService dailyMonitoringService; private String startTime = null; private String endTime = null; public String getStartTime() { return startTime; } public void setStartTime(String startTime) { this.startTime = startTime; } public String getEndTime() { return endTime; } public void setEndTime(String endTime) { this.endTime = endTime; } }
而後你看咱們的Action中,
/*SpringMVC傳遞參數和Struts傳遞參數 不一樣; Struts會調用setter方法來將值返回*/ public String loadAssetsTransactionsByRegionTime() { lineVM = new LineChartVM(); lineVM.setTitle("Assets Transactions By Region"); lineVM.setyAxisName("Transactions"); Map<String, Map<String, Double>> assetRegionMap = dailyMonitoringService .loadAssetRegionTransactionTime(startTime, endTime); lineVM.setCategories(new ArrayList<String>(assetRegionMap.keySet())); Map<String, List<Double>> seriesMap = pivotingMap(assetRegionMap, 0D); List<ChartSerieVM> seriesList = new ArrayList<ChartSerieVM>(); for (String key : seriesMap.keySet()) { ChartSerieVM chartSerieVM = new ChartSerieVM(); chartSerieVM.setName(key); chartSerieVM.setData(seriesMap.get(key)); seriesList.add(chartSerieVM); } lineVM.setSeries(seriesList); return SUCCESS; }
你看咱們的startTime和endTime是直接使用的,沒有在函數的參數中寫,並且定義的時候咱們定義的是private String endTime = null;可是使用的時候,值就這麼直接傳遞了進來,就是這麼神奇啊。另外兩種參數傳遞方法是ActionContext.getContext().put("startTime","2015-06-01");ActionContext.getContext.put("endTime","2015-06-10");(其中put進去的一對對的鍵值對)和經過Servlet的API來傳值(ServletActionContext.getRequest.setAttribute("startTime","2015-06-01");ServletActionContext.getRequest.setAttribute("endTime","2015-06-10");)
前臺在展示數據時候,能夠有以下幾種方法:
1.${startTime} ${endTime}直接取值.
2.經過struts2的標籤庫<%@taglib prefix="s" uri="/struts-tags"%> 引入struts2jar包中的一個tags標籤庫,而後使用以下方式:
<s:property value="#startTime"> <s:property value="#endTime">
就能夠將數據展示出來。(注意這裏的value中的變量名前面要加‘#’號。)
備註:對於ServletActionContext.getRequest.setAttribute("endTime","2015-06-10");這種取值方式,在前臺展現的時候須要這樣來用,以下:
<s:property value="#request.endTime">
************************************************************************************************************************************
接下來咱們看看Struts中最核心的知識點:
:
鳴謝:
參考博客(http://www.cnblogs.com/suxiaolei/archive/2011/10/28/2228063.html)