Spring 2.0集成Struts 2.0

Struts2在經歷了兩年漫長的開發後,終於在2007年2月底正式發佈,Struts 1.3.8是Struts 1的最終版本,Struts 1今後再也不升級。Struts 2和Struts 1差異很是大,幾乎是兩個徹底不一樣的東西,因此不要期望Struts 1在不通過修改的狀況下就能夠跑在Struts 2環境下。Struts 2與其說是Struts的升級版本,莫如說是WebWork的升級版本——咱們能夠經過WebWork開發團隊當年宣稱併入Struts時所提到的一句話瞭解到這一點:「WebWork 擁有前衛的技術,Struts 擁有強大的用戶羣,二者的合併或許將是一個完美的組合……」。html

Struts 2概述java

衆所周知,Struts 2是Struts和WebWork的混血兒, 而在Webwork 2.2以前的Webwork版本,其自身有一套IoC實現,在Spring 框架如火如荼發展的背景下,Webwork決定放棄控制反轉功能的開發,轉由使用Spring的實現。有愈來愈多的開源組件(如iBATIS等)都放棄與Spring重疊功能的開發,所以,Struts 2推薦你們經過Spring實現IoC功能。
Struts 2 擁有衆多的新特性,下面咱們對此進行歸納性的描述:web

? ·Action類更加靈活spring

Struts 2的Action類能夠實現一個Action接口,也可實現其餘接口,所以很容易添加定製性的服務。Struts 2提供一個ActionSupport,它是實現了Struts經常使用接口的方便類。Action接口不是必需的,任何具備execute簽名的POJO均可以用做Struts 2的Action對象。數據庫

? ·每一請求對應一個Action實例apache

Struts 1的Action以單例模式運行,全部的請求對應同一個Action實例。所以用戶必須保證Action自己是線程安全的。Struts 2 的Action對象爲每個請求產生一個實例,所以沒有線程安全問題,它至關於Spring MVC的ThrowawayController。瀏覽器

? ·不依賴於Servlet 容器安全

Struts 1 Action 依賴於Servlet 容器,但Struts 2 Action不依賴於Servlet容器,容許Action脫離容器單獨測試。若是須要,Struts2的Action仍然能夠訪問到request和response實例。app

? ·具備很強的可測試性框架

因爲Struts 1依賴於Servlet容器,因此很難脫離容器進行測試,雖然能夠經過StrutsTestCase使用模擬對象進行測試,但操做依舊比較麻煩。Struts 2的Action不依賴於Servlet容器,用戶能夠簡單地經過初始化、設置屬性、調用方法進行測試,同時「依賴注入」的支持也使測試更加容易。

? ·捕獲輸入更加靈活

Struts 1使用ActionForm對象捕獲輸入,全部的ActionForm必須繼承一個基類。Struts 2直接使用Action屬性綁定請求參數,消除了對第二個輸入對象的需求。這種模型驅動的特性簡化了頁面標籤對POJO輸入對象的引用。

? ·表達式語言更強大

Struts1整合了JSTL,所以可使用JSTL EL。這種EL有基本對象圖遍歷的功能,可是對集合和索引屬性的支持很弱。Struts2依舊可使用JSTL,可是也支持一個更強大和靈活的對象屬性引用語言——「Object Graph Notation Language(OGNL)」。

? ·實現值和頁面的靈活綁定

Struts 1使用標準JSP機制把對象綁定到頁面中進行訪問。Struts 2使用「值堆棧」技術,使標籤可以訪問值而不須要把頁面和對象綁定起來。「值堆棧」策略容許經過一系列名稱相同但類型不一樣的屬性重用頁面。

? ·類型轉換更加方便

Struts 1 的ActionForm屬性一般都是String類型,Struts1使用Commons-Beanutils進行類型轉換。每一個類對應一個轉換器,對每個實例來講是不可配置的。Struts2使用OGNL進行類型轉換。提供基本和經常使用對象的轉換器。

? ·數據校驗更加方便

Struts 1支持在ActionForm的validate()方法中進行校驗,或者經過Commons Validator的擴展進行校驗。同一個類能夠有不一樣的校驗內容,但不能校驗子對象。Struts2支持經過validate()方法和XWork校驗框架來進行校驗。

? ·Action的生命週期更加靈活

Struts1支持每個模塊有單獨的生命週期,可是模塊中的全部Action必須共享相同的生命週期。Struts 2支持經過攔截器堆棧爲每個Action建立不一樣的生命週期。堆棧可以根據須要和不一樣的Action一塊兒使用。

集成Struts 2的步驟

和Spring集成的目標是什麼呢?無非是但願Struts 2的Action定義直接使用Spring IoC的功能,將業務層的Bean注入到Struts 的Action中。用戶固然能夠簡單地在Struts中經過WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)獲取Spring容器的Bean,手工將其裝配到Action中,但這種「集成」屬於低層次的集成,咱們要達到的集成目標是:在Spring配置文件中配置Struts Action,並在Struts配置文件中引用。

Spring 2.1目前僅提供了對Struts 1集成的實現方案,還沒有對Struts 2提供集成支持。不過Spring或許能夠永遠卸下這副擔子,由於Struts 2已經提供了集成到Spring中的插件,主動投懷送抱了。歷史老是讓人玩味,在開發初期,Spring不厭其煩,殫精竭慮地爲各類著名的框架提供集成的實現方案,等到如今Spring強大以後,各類框架反過來主動提供了集成到Spring的方案。
要將Struts 2集成到Spring中,須要完成如下三個基本步驟:

1.將如下Struts類包添加到類路徑下

struts2-core-2.0.6.jar
xwork-2.0.1.jar
ognl-2.6.11.jar
struts2-spring-plugin-2.0.6.jar
freemarker-2.3.8.jar
其中struts2-spring-plugin-2.0.6.jar就是Struts 2提供的用於集成到Spring中的類包。該類包擁有一個struts-plugin.xml配置文件,它定義了一個名爲spring的StrutsSpring ObjectFactory的Bean,以便將Action類的管理工做委託給Spring容器進行處理。也就是說,經過StrutsSpringObjectFactory的配置,Struts配置文件就能夠直接引用Spring容器中的Bean了。

2.編寫Struts配置文件struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> ① 經過這個配置指定使用struts-plugin.xml中的StrutsSpringObjectFactory做爲 建立Action的工廠類 <constant name= "struts.objectFactory" value="spring" /> … </struts>
將其命名爲struts.xml文件,並保存在<項目根目錄>/src下。

3.配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2...; xmlns:xsi="http://www.w3.org/2001/XMLSch...; xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <context-param> ①指定Spring配置文件 <param-name>contextConfigLocation</param-name> <param-value>classpath:baobaotao-*.xml,</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> ② 配置Struts2 的過濾器 <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
咱們在類路徑下提供了幾個Spring配置文件:其中baobaotao-service.xml用於配置業務層Bean,而baobaotao-web.xml用於配置Struts的Action。在①處,經過contextConfigLocation指定Spring配置文件,並配置ContextLoaderListener啓動Spring容器。
咱們知道Struts 1使用基於Servlet的ActionServlet啓動Struts框架,Struts 2使用基於Filer的FilterDispatcher完成相同的工做。FilterDispatcher會自動使用類路徑下的struts.xml初始化Struts框架。

經過以上三個基本步驟,咱們就將Struts 2集成到Spring中了,但這只是一個基礎的空架子,Spring究竟如何配置Struts的Action,Struts又如何引用Spring中配置好的Action,這些具體問題有待於經過實例進行具體的說明。

註冊用戶實例

下面咱們使用一個小例子介紹Struts集成到Spring中的具體應用。

建立Action對象

首先,咱們須要編寫一個可以處理表單提交的Action類,Struts 2的Action類是表單對象和控制器的混合體,Action類自己的屬性就能夠和表單數據進行綁定。不過爲了保證業務層接口和Web層無關,通常狀況下,咱們仍是須要編寫一個獨立的表單對象,只要將表單對象做爲Action的屬性,就能夠經過級聯屬性的方式綁定頁面的表單數據了。來看一下負責處理表單提交的UserRegisterAction的代碼:
代碼清單1 UserRegisterAction:用戶註冊Action package com.baobaotao.web.user; import com.baobaotao.domain.User; import com.baobaotao.service.BbtForum; import com.opensymphony.xwork2.ActionSupport; public class UserRegisterAction extends ActionSupport { private BbtForum bbtForum; ①業務層的Bean private User user; ②充當表單對象的POJO屬性對象 @Override public String execute() throws Exception { ③Action處理請求的方法 if (user == null) { ③-1當user爲null時,導向到輸入表單頁面 user = new User(); return INPUT; } else { ③-2當user不爲null時,處理表單提交 System.out.println("user:"+user); bbtForum.registerUser(user); return SUCCESS; } } public void setBbtForum(BbtForum bbtForum) { this.bbtForum = bbtForum; } public void setUser(User user) { this.user = user; } public User getUser() { return user; } }
在UserRegisterAction中,咱們定義了一個業務層BbtForum對象和用於綁定表單數據的User對象。在後續章節中,讀者將看到咱們如何在Spring中配置UserRegisterAction以及將Spring容器中的BbtForum Bean注入到UserRegisterAction中。用戶也能夠直接在UserRegisterAction中聲明和表單組件相對應的屬性(如userName、password等),不過因爲咱們須要調用業務層對象的方法操做表單數據,因此最好仍是獨立到一個POJO中,這即是User對象。若是請求參數僅是一些控制性數據,徹底能夠直接經過Action標量屬性進行綁定。User對象是一個POJO,其代碼以下所示:
package com.baobaotao.domain; public class User { private String userName; private String password; private String email; //省缺get/setter }
在編寫好Struts 2的Action後,咱們下一步要作的是在Spring容器中配置這個Action,以便充分享用Spring容器的各項功能。
配置Action
首先咱們在baobaotao-web.xml的Spring配置文件中配置UserRegisterAction Bean:
①做用域必須設置爲prototype <bean id="registerUserAction" class="com.baobaotao.web.user.UserRegisterAction" scope="prototype"> <property name="bbtForum" ref="bbtForum" /> ②註冊Spring容器中的Bean </bean>
特別須要注意的是必須將UserRegisterAction Bean的做用域定義爲prototype,由於UserRegisterAction是有狀態的,必須每個請求對應一個新的UserRegisterAction 實例。bbtForum是在baobaotao-service.xml中配置的業務層Bean,在此將其注入到Action中。

既然咱們已經在Spring中配置好了Struts 2的Action,接下來就碰到了最核心的問題:如何在Struts配置文件中引用這個Bean呢?也許看一眼struts.xml配置文件就水落石出了:
代碼清單2 struts.xml:配置Action <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.objectFactory" value="spring" /> <package name="user" namespace="/user" extends="struts-default"> ① 擴展默認配置 ②引用Spring容器中的Bean <action name="registerUserAction" class="registerUserAction"> <result name ="input">/ WEB-INF /jsp/ registerUser.jsp</result> ③表單輸入頁面 <result name=" success">/WEB-INF/jsp/success.jsp</result> ④操做成功轉向的頁面 </action> </package> </struts>
①處擴展了struts-default,Struts 2在struts2-core-2.0.6.jar中定義了一個struts-default.xml配置文件,定義了Struts一些默認的基礎設施,在這裏咱們經過擴展這個配置文件提供額外的配置。②處的class="registerUserAction"也許是最使人好奇的,在正常狀況下,Struts經過class屬性指定Action的實現類,形如:
<action name="registerUserAction" class="com.baobaotao.web.user.UserRegisterAction">
但此時class屬性卻不是一個類名,相反它是一個相似於Bean名的字符串,祕密偏偏隱藏在這裏:事實上class屬性的值正是指向Spring容器中的Bean名稱,在後臺StrutsSpringObjectFactory經過相似於如下的代碼得到真實的Action實例:
WebApplicationContext ctx= ApplicationContextUtils.etWebApplicationContext(ServletContext sc); UserRegisterAction userRegisterAction = (UserRegisterAction) ctx.getBean (「registerUserAction」);
經過這種集成方式,Struts 2和Spring就渾然一體、水乳交融了。在編寫並配置好Action後,接下來,咱們來看一下用戶註冊信息輸入的JSP表單頁面。
表單頁面和成功頁面

用於填寫用戶註冊信息的表單頁面須要使用Struts 2的標籤,這樣就能夠和服務端的Action屬性進行綁定。來看一下registerUser.jsp頁面的代碼:
代碼清單3 registerUser.jsp:用戶註冊表單頁面 <%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> ①聲明Struts標籤 <html> <head><title>寶寶淘註冊頁面</title></head> <body> ② 引用struts.xml中定義的Action <s:form> <s:textfield key=" user.userName" label="用戶名"/> <s:password key="user.password" label="密 碼"/> <s:textfield key="user.email" label="Email"/> <s:submit/> </s:form> </body> </html>
首先,在①處聲明Struts標籤,②處的代碼構造了一個表單。讀者是否發現Struts 2的表單標籤和Spring MVC的表單標籤很相像呢?表單標籤<s:form>也僅須要一個簡單的標籤聲明就能夠了,無須經過action屬性指定表單提交的地址,表單組件標籤也經過級聯屬性的方式綁定表單對象。惟一不太相同的是,表單組件標籤有一個label屬性,生成表單時,它將做爲組件前頭的一個文本標籤,這種將HTML組件標籤和組件自己捆綁的方式頗有趣,不過好像綁定得過於緊密了,不利於HTML頁面設計。

那如何訪問到這個表單頁面呢?答案是和Spring MVC的表單控制器同樣,經過直接請求Action,Action發現User是null時自動導向到表單輸入頁面中,這樣<s:form>標籤就能夠肯定表單提交的地址了。可見事件發展到最後每每是異曲同工,或說英雄所見略同。

當表單提供處理完成後,咱們轉向一個success.jsp頁面,這個頁面顯示註冊用戶的歡迎信息:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Welcome</title> </head> <body> ① 顯示用戶名屬性 <s:property value="user.userName"/> Welcome to baobaotao </body> </html>
在①處,咱們經過<s:property>標籤顯示錶單對象的userName屬性,因爲級聯屬性的根是相對於Action而言的,因此必須設置爲「user.UserName」。

將registerUser.jsp、success.jsp放置到WEB-INF/jsp目錄下,這樣就知足代碼清單2的③和④中的配置了。

測試用戶註冊功能

在瀏覽器地址欄中輸入http://localhost/baobaotao/us...,因爲第一次調用時,UserRegisterAction中的user屬性爲null,請求將被導向到registerUser.jsp頁面中,如圖1所示:

經過這個表單頁面,對照代碼清3 ②處的編寫方式,咱們就能夠清楚地知道Struts 2表單組件標籤的label屬性究竟對應HTML頁面中的哪一個內容了。

在點擊Submit提交表單後,RegisterUserAction負責處理表單提交,並調用BbtForum# registerUser(User user)將User對象保存到數據庫中,而後導向到success.jsp頁面上。

**小結 ** 和Spring採用主動提供插件的方式集成Struts 1.x,時至今日Struts 2.0卻反過來提供了一個集成到Spring中的插件包。應該說,和集成Struts 1.x相比,集成Struts 2.0更加容易了,你只須要使用如下兩個步驟就能夠了: 1) 將struts2-spring-plugin-2.0.6.jar添加到類路徑中; 2)在struts.xml配置文件中添加如下配置: <constant name="struts.objectFactory" value="spring" /> 在Struts的<action>配置項中,原來的class屬性指定爲一個Action實現類,集成Spring後,class屬性直接指定Spring容器中Bean的名稱。

相關文章
相關標籤/搜索