J2EE學習Struts2技術詳解

摘要

Struts2是在WebWork2基礎發展而來的。和struts1一樣, Struts2也屬於MVC框架。不過有一點大家需要注意的是:儘管Struts2和Struts1在名字上的差別不是很大,但Struts2和struts1在代碼編寫風格上幾乎是不一樣的。那麼既然有了struts1,爲何還要推出struts2。主要是因爲struts2有以下優點:

1 > 在軟件設計上Struts2沒有像struts1那樣跟Servlet API和struts API有着緊密的耦合,Struts2的應用可以不依賴於Servlet API和struts API。 Struts2的這種設計屬於無侵入式設計,而Struts1卻屬於侵入式設計。
  1. public class OrderListAction extends Action {  
  2.     public ActionForward execute(ActionMapping mapping, ActionForm form,  
  3.             HttpServletRequest request, HttpServletResponse response)  
  4.             throws Exception {  
  5.     }  
  6. }  
2> Struts2提供了攔截器,利用攔截器可以進行AOP編程,實現如權限攔截等功能。
3> Strut2提供了類型轉換器,我們可以把特殊的請求參數轉換成需要的類型。在Struts1中,如果我們要實現同樣的功能,就必須向Struts1的底層實現BeanUtil註冊類型轉換器才行。
4> Struts2提供支持多種表現層技術,如:JSP、freeMarker、Velocity等
5> Struts2的輸入校驗可以對指定方法進行校驗,解決了Struts1長久之痛。

6> 提供了全局範圍、包範圍和Action範圍的國際化資源文件管理實現


搭建Struts2開發環境

搭建Struts2環境時,我們一般需要做以下幾個步驟的工作:
1》找到開發Struts2應用需要使用到的jar文件.
2》編寫Struts2的配置文件
3》在web.xml中加入Struts2 MVC框架啓動配置


搭建Struts2開發環境--開發Struts2應用依賴的jar文件

大家可以到http://struts.apache.org/download.cgi#struts2014下載struts-2.x.x-all.zip,目前最新版爲2.1.8。下載完後解壓文件,開發struts2應用需要依賴的jar文件在解壓目錄的lib文件夾下。不同的應用需要的JAR包是不同的。下面給出了開發Struts 2程序最少需要的JAR。
struts2-core-2.x.x.jar :Struts 2框架的核心類庫
xwork-core-2.x.x.jar :XWork類庫,Struts 2在其上構建
ognl-2.6.x.jar :對象圖導航語言(Object Graph Navigation Language),struts2框架通過其讀寫對象的屬性
freemarker-2.3.x.jar :Struts 2的UI標籤的模板使用FreeMarker編寫
commons-logging-1.x.x.jar :ASF出品的日誌包,Struts 2框架使用這個日誌包來支持Log4J和JDK 1.4+的日誌記錄。
commons-fileupload-1.2.1.jar 文件上傳組件,2.1.6版本後必須加入此文件


搭建Struts2開發環境-- Struts2應用的配置文件

Struts2默認的配置文件爲struts.xml ,該文件需要存放在WEB-INF/classes下,該文件的配置模版如下:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE struts PUBLIC  
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  
  4.     "http://struts.apache.org/dtds/struts-2.0.dtd">  
  5. <struts>  
  6. </struts>  

搭建Struts2開發環境--Struts2在web中的啓動配置

在struts1.x中, struts框架是通過Servlet啓動的。在struts2中,struts框架是通過Filter啓動的。他在web.xml中的配置如下:

  1. <filter>  
  2.     <filter-name>struts2</filter-name>  
  3.     <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  4.  <!-- 自從Struts 2.1.3以後,下面的FilterDispatcher已經標註爲過時  
  5.     <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> -->   
  6. </filter>  
  7. <filter-mapping>  
  8.     <filter-name>struts2</filter-name>  
  9.     <url-pattern>/*</url-pattern>  
  10. </filter-mapping>  

在StrutsPrepareAndExecuteFilter的init()方法中將會讀取類路徑下默認的配置文件struts.xml完成初始化操作。

注意:struts2讀取到struts.xml的內容後,以javabean形式存放在內存中,以後struts2對用戶的每次請求處理將使用內存中的數據,而不是每次都讀取struts.xml文件


第一個Struts2應用--HelloWorld

在默認的配置文件struts.xml 中加入如下配置:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE struts PUBLIC  
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  
  4.     "http://struts.apache.org/dtds/struts-2.0.dtd">  
  5. <struts>  
  6.    <package name="itcast" namespace="/test" extends="struts-default">  
  7.         <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >  
  8.     <result name="success">/WEB-INF/page/hello.jsp</result>  
  9.         </action>  
  10.     </package>   
  11. </struts>  

Struts.xml配置中的包介紹

  1. <package name="itcast" namespace="/test" extends="struts-default">  
  2. <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >  
  3.     <result name="success">/WEB-INF/page/hello.jsp</result>  
  4. </action>  
  5. </package>   

在struts2框架中使用包來管理Action,包的作用和java中的類包是非常類似的,它主要用於管理一組業務功能相關的action。在實際應用中,我們應該把一組業務功能相關的Action放在同一個包下。


name屬性
配置包時必須指定name屬性,該name屬性值可以任意取名,但必須唯一,他不對應java的類包,如果其他包要繼承該包,必須通過該屬性進行引用。

namespace屬性

包的namespace屬性用於定義該包的命名空間,命名空間作爲訪問該包下Action的路徑的一部分,如訪問上面例子的Action,訪問路徑爲:/test/helloworld.action。 namespace屬性可以不配置,對本例而言,如果不指定該屬性,默認的命名空間爲“”(空字符串)。
通常每個包都應該繼承struts-default包, 因爲Struts2很多核心的功能都是攔截器來實現。如:從請求中把請求參數封裝到action、文件上傳和數據驗證等等都是通過攔截器實現的。 struts-default定義了這些攔截器和Result類型。可以這麼說:當包繼承了struts-default才能使用struts2提供的核心功能。 struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定義。 struts-default.xml也是Struts2默認配置文件。 Struts2每次都會自動加載 struts-default.xml文件。
包還可以通過abstract=“true”定義爲抽象包,抽象包中不能包含action。


例子中使用到的cn.itcast.action.HelloWorldAction類如下:

  1. package cn.itcast.action;  
  2. public class HelloWorldAction{  
  3.     private String message;  
  4.       
  5.     public String getMessage() {  
  6.         return message;  
  7.     }  
  8.     public void setMessage(String message) {  
  9.         this.message = message;  
  10.     }  
  11.   
  12.   
  13.     public String execute() {  
  14.         this.message = "我的第一個struts2應用";  
  15.         return "success";  
  16.     }  
  17. }  

例子中使用到的/WEB-INF/page/hello.jsp如下:

  1. <%@ page language="java" pageEncoding="UTF-8"%>  
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  3. <html>  
  4.   <head>  
  5.     <title>第一個struts2應用</title>  
  6.   </head>  
  7.   <body>  
  8.    ${message } <br>  
  9.   </body>  
  10. </html>  

可以使用EL表達式訪問Action中的屬性。


訪問HelloWorld應用

在struts1中,通過<action path=“/test/helloworld”>節點的path屬性指定訪問該action的URL路徑。在struts2中,情況就不是這樣了,訪問struts2中action的URL路徑由兩部份組成:包的命名空間+action的名稱,例如訪問本例子HelloWorldAction的URL路徑爲:/test/helloworld (注意:完整路徑爲:http://localhost:端口/內容路徑/test/helloworld)。另外我們也可以加上.action後綴訪問此Action。

  1. <package name="itcast" namespace="/test" extends="struts-default">  
  2.        <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >  
  3.     <result name="success">/WEB-INF/page/hello.jsp</result>  
  4.        </action>  
  5. </package>   


Action名稱的搜索順序

1.獲得請求路徑的URI,例如url是:http://server/struts2/path1/path2/path3/test.action
2.首先尋找namespace爲/path1/path2/path3的package,如果不存在這個package則執行步驟3;如果存在這個package,則在這個package中尋找名字爲test的action,當在該package下尋找不到action 時就會直接跑到默認namaspace的package裏面去尋找action(默認的命名空間爲空字符串“” ) ,如果在默認namaspace的package裏面還尋找不到該action,頁面提示找不到action 
3.尋找namespace爲/path1/path2的package,如果不存在這個package,則轉至步驟4;如果存在這個package,則在這個package中尋找名字爲test的action,當在該package中尋找不到action 時就會直接跑到默認namaspace的package裏面去找名字爲test的action ,在默認namaspace的package裏面還尋找不到該action,頁面提示找不到action 
4.尋找namespace爲/path1的package,如果不存在這個package則執行步驟5;如果存在這個package,則在這個package中尋找名字爲test的action,當在該package中尋找不到action 時就會直接跑到默認namaspace的package裏面去找名字爲test的action ,在默認namaspace的package裏面還尋找不到該action,頁面提示找不到action 
5.尋找namespace爲/的package,如果存在這個package,則在這個package中尋找名字爲test的action,當在package中尋找不到action或者不存在這個package時,都會去默認namaspace的package裏面尋找action,如果還是找不到,頁面提示找不到action。


Action配置中的各項默認值

  1. <package name="itcast" namespace="/test" extends="struts-default">  
  2.         <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >  
  3.     <result name="success">/WEB-INF/page/hello.jsp</result>  
  4.         </action>  
  5.   </package>   
1>如果沒有爲action指定class,默認是ActionSupport。
2>如果沒有爲action指定method,默認執行action中的execute() 方法。
3>如果沒有指定result的name屬性,默認值爲success。


Action中result的各種轉發類型

  1. <action name="helloworld" class="cn.itcast.action.HelloWorldAction">  
  2.     <result name="success">/WEB-INF/page/hello.jsp</result>  
  3. </action>  
result配置類似於struts1中的forward,但struts2中提供了多種結果類型,常用的類型有: dispatcher(默認值)、 redirect 、 redirectAction 、 plainText。


在result中還可以使用${屬性名}表達式訪問action中的屬性,表達式裏的屬性名對應action中的屬性。如下:
<result type="redirect">/view.jsp?id=${id}</result>

下面是redirectAction 結果類型的例子,如果重定向的action中同一個包下: 
  1. <result type="redirectAction">helloworld</result>  

如果重定向的action在別的命名空間下:
  1. <result type="redirectAction">  
  2.     <param name="actionName">helloworld</param>  
  3.     <param name="namespace">/test</param>  
  4. </result>  


plaintext:顯示原始文件內容,例如:當我們需要原樣顯示jsp文件源代碼 的時候,我們可以使用此類型。
  1. <result name="source" type="plainText ">  
  2.     <param name="location">/xxx.jsp</param>  
  3.     <param name="charSet">UTF-8</param><!-- 指定讀取文件的編碼 -->  
  4. </result>  


多個Action共享一個視圖--全局result配置

當多個action中都使用到了相同視圖,這時我們應該把result定義爲全局視圖。struts1中提供了全局forward,struts2中也提供了相似功能:

  1. <package ....>  
  2.     <global-results>  
  3.         <result name="message">/message.jsp</result>  
  4.     </global-results>  
  5. </package>  


Action的屬性注入值

Struts2爲Action中的屬性提供了依賴注入功能,在struts2的配置文件中,我們可以很方便地爲Action中的屬性注入值。注意:屬性必須提供setter方法。

  1. public class HelloWorldAction{  
  2.     private String savePath;  
  3.   
  4.   
  5.     public String getSavePath() {  
  6.         return savePath;  
  7.     }  
  8.     public void setSavePath(String savePath) {  
  9.         this.savePath = savePath;  
  10.     }  
  11.        ......  
  12. }  

  1. <package name="itcast" namespace="/test" extends="struts-default">  
  2.     <action name="helloworld" class="cn.itcast.action.HelloWorldAction" >  
  3.         <param name="savePath">/images</param>  
  4.         <result name="success">/WEB-INF/page/hello.jsp</result>  
  5.     </action>  
  6. </package>  

上面通過<param>節點爲action的savePath屬性注入“/images”


指定需要Struts 2處理的請求後綴

前面我們都是默認使用.action後綴訪問Action。其實默認後綴是可以通過常量”struts.action.extension“進行修改的,例如:我們可以配置Struts 2只處理以.do爲後綴的請求路徑:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE struts PUBLIC  
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  
  4.     "http://struts.apache.org/dtds/struts-2.0.dtd">  
  5. <struts>  
  6.     <constant name="struts.action.extension" value="do"/>  
  7. </struts>  

如果用戶需要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開。如:
  1. <constant name="struts.action.extension" value="do,go"/>  


細說常量定義

常量可以在struts.xml或struts.properties中配置,建議在struts.xml中配置,兩種配置方式如下:
在struts.xml文件中配置常量

  1. <struts>  
  2.     <constant name="struts.action.extension" value="do"/>  
  3. </struts>  

在struts.properties中配置常量
struts.action.extension=do

因爲常量可以在下面多個配置文件中進行定義,所以我們需要了解struts2加載常量的搜索順序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
如果在多個文件中配置了同一個常量,則後一個文件中配置的常量值會覆蓋前面文件中配置的常量值.


常用的常量介紹

<!-- 指定默認編碼集,作用於HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的輸出 -->
    <constant name="struts.i18n.encoding" value="UTF-8"/>
    <!-- 該屬性指定需要Struts 2處理的請求後綴,該屬性的默認值是action,即所有匹配*.action的請求都由Struts2處理。
    如果用戶需要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開。 -->
    <constant name="struts.action.extension" value="do"/>
    <!-- 設置瀏覽器是否緩存靜態內容,默認值爲true(生產環境下使用),開發階段最好關閉 -->
    <constant name="struts.serve.static.browserCache" value="false"/>
    <!-- 當struts的配置文件修改後,系統是否自動重新加載該文件,默認值爲false(生產環境下使用),開發階段最好打開 -->
    <constant name="struts.configuration.xml.reload" value="true"/>
    <!-- 開發模式下使用,這樣可以打印出更詳細的錯誤信息 -->
    <constant name="struts.devMode" value="true" />
     <!-- 默認的視圖主題 -->
    <constant name="struts.ui.theme" value="simple" />
    <!– 與spring集成時,指定由spring負責action對象的創建 -->
    <constant name="struts.objectFactory" value="spring" />
 <!–該屬性設置Struts 2是否支持動態方法調用,該屬性的默認值是true。如果需要關閉動態方法調用,則可設置該屬性爲false。 -->
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
 <!--上傳文件的大小限制-->
<constant name="struts.multipart.maxSize" value=“10701096"/>

Struts2的處理流程


StrutsPrepareAndExecuteFilter是Struts 2框架的核心控制器,它負責攔截由<url-pattern>/*</url-pattern>指定的所有用戶請求,當用戶請求到達時,該Filter會過濾用戶的請求。默認情況下,如果用戶請求的路徑不帶後綴或者後綴以.action結尾,這時請求將被轉入Struts 2框架處理,否則Struts 2框架將略過該請求的處理。當請求轉入Struts 2框架處理時會先經過一系列的攔截器,然後再到Action。與Struts1不同,Struts2對用戶的每一次請求都會創建一個Action,所以Struts2中的Action是線程安全的。

爲應用指定多個struts配置文件

在大部分應用裏,隨着應用規模的增加,系統中Action的數量也會大量增加,導致struts.xml配置文件變得非常臃腫。爲了避免struts.xml文件過於龐大、臃腫,提高struts.xml文件的可讀性,我們可以將一個struts.xml配置文件分解成多個配置文件,然後在struts.xml文件中包含其他配置文件。下面的struts.xml通過<include>元素指定多個配置文件:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE struts PUBLIC  
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  
  4.     "http://struts.apache.org/dtds/struts-2.0.dtd">  
  5. <struts>  
  6.     <include file="struts-user.xml"/>  
  7.     <include file="struts-order.xml"/>  
  8. </struts>  

通過這種方式,我們就可以將Struts 2的Action按模塊添加在多個配置文件中。


動態方法調用

如果Action中存在多個方法時,我們可以使用!+方法名調用指定方法。如下:

  1. public class HelloWorldAction{  
  2.     private String message;  
  3.     ....  
  4.     public String execute() throws Exception{  
  5.         this.message = "我的第一個struts2應用";  
  6.         return "success";  
  7.     }  
  8.       
  9.     public String other() throws Exception{  
  10.         this.message = "第二個方法";  
  11.         return "success";  
  12.     }  
  13. }  

假設訪問上面action的URL路徑爲: /struts/test/helloworld.action
要訪問action的other() 方法,我們可以這樣調用:
/struts/test/helloworld!other.action
如果不想使用動態方法調用,我們可以通過常量struts.enable.DynamicMethodInvocation關閉動態方法調用。
  1. <constant name="struts.enable.DynamicMethodInvocation" value="false"/>  

使用通配符定義action

  1. <package name="itcast" namespace="/test" extends="struts-default">  
  2.     <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">  
  3.         <result name="success">/WEB-INF/page/hello.jsp</result>  
  4.     </action>  
  5. </package>  

  1. public class HelloWorldAction{  
  2.     private String message;  
  3.     ....  
  4.     public String execute() throws Exception{  
  5.         this.message = "我的第一個struts2應用";  
  6.         return "success";  
  7.     }  
  8.       
  9.     public String other() throws Exception{  
  10.         this.message = "第二個方法";  
  11.         return "success";  
  12.     }  
  13. }  

要訪問other()方法,可以通過這樣的URL訪問:/test/helloworld_other.action


接收請求參數

採用基本類型接收請求參數(get/post)

在Action類中定義與請求參數同名的屬性,struts2便能自動接收請求參數並賦予給同名屬性。
請求路徑: http://localhost:8080/test/view.action?id=78
  1. public class ProductAction {  
  2.       private Integer id;  
  3.       public void setId(Integer id) {//struts2通過反射技術調用與請求參數同名的屬性的setter方法來獲取請求參數值  
  4.              this.id = id;  
  5.       }  
  6.       public Integer getId() {return id;}  
  7.   }  

採用複合類型接收請求參數

請求路徑: http://localhost:8080/test/view.action?product.id=78
  1.  public class ProductAction {  
  2.    private Product product;  
  3.    public void setProduct(Product product) {  this.product = product;  }  
  4.    public Product getProduct() {return product;}  
  5. }  

Struts2首先通過反射技術調用Product的默認構造器創建product對象,然後再通過反射技術調用product中與請求參數同名的屬性的setter方法來獲取請求參數值。


關於struts2.1.6接收中文請求參數亂碼問題

struts2.1.6版本中存在一個Bug,即接收到的中文請求參數爲亂碼(以post方式提交),原因是struts2.1.6在獲取並使用了請求參數後才調用HttpServletRequest的setCharacterEncoding()方法進行編碼設置 ,導致應用使用的就是亂碼請求參數。這個bug在struts2.1.8中已經被解決,如果你使用的是struts2.1.6,要解決這個問題,你可以這樣做:新建一個Filter,把這個Filter放置在Struts2的Filter之前,然後在doFilter()方法裏添加以下代碼

  1. public void doFilter(...){  
  2.     HttpServletRequest req = (HttpServletRequest) request;  
  3.     req.setCharacterEncoding("UTF-8");//應根據你使用的編碼替換UTF-8  
  4.     filterchain.doFilter(request, response);  
  5. }  


自定義類型轉換器

java.util.Date類型的屬性可以接收格式爲2009-07-20的請求參數值。但如果我們需要接收格式爲20091221的請求參數,我們必須定義類型轉換器,否則struts2無法自動完成類型轉換。

  1. import java.util.Date;  
  2. public class HelloWorldAction {  
  3.     private Date createtime;  
  4.   
  5.     public Date getCreatetime() {  
  6.         return createtime;  
  7.     }  
  8.   
  9.     public void setCreatetime(Date createtime) {  
  10.         this.createtime = createtime;  
  11.     }  
  12. }  

  1. public class DateConverter extends DefaultTypeConverter {  
  2.                 @Override  public Object convertValue(Map context, Object value, Class toType) {  
  3.     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");  
  4.     try {   
  5.         if(toType == Date.class){//當字符串向Date類型轉換時  
  6.             String[] params = (String[]) value;// Request.getParameterValues()   
  7.             return dateFormat.parse(params[0]);  
  8.         }else if(toType == String.class){//當Date轉換成字符串時  
  9.             Date date = (Date) value;  
  10.             return dateFormat.format(date);  
  11.         }  
  12.     } catch (ParseException e) {}  
  13.     return null;  
  14.     }  
  15. }  

將上面的類型轉換器註冊爲局部類型轉換器:
在Action類所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的類名,後面的-conversion.properties是固定寫法,對於本例而言,文件的名稱應爲HelloWorldAction-conversion.properties 。在properties文件中的內容爲:
屬性名稱=類型轉換器的全類名
對於本例而言, HelloWorldAction-conversion.properties文件中的內容爲:
createtime= cn.itcast.conversion.DateConverter


自定義全局類型轉換器

將上面的類型轉換器註冊爲全局類型轉換器:
在WEB-INF/classes下放置xwork-conversion.properties文件 。在properties文件中的內容爲:
待轉換的類型=類型轉換器的全類名
對於本例而言, xwork-conversion.properties文件中的內容爲:
java.util.Date= cn.itcast.conversion.DateConverter


訪問或添加request/session/application屬性

  1. public String scope() throws Exception{  
  2.    ActionContext ctx = ActionContext.getContext();  
  3.    ctx.getApplication().put("app""應用範圍");//往ServletContext裏放入app  
  4.    ctx.getSession().put("ses""session範圍");//往session裏放入ses  
  5.    ctx.put("req""request範圍");//往request裏放入req  
  6.    return "scope";  
  7. }  

JSP:
  1. <body>  
  2.    ${applicationScope.app} <br>  
  3.    ${sessionScope.ses}<br>  
  4.    ${requestScope.req}<br>  
  5. </body>  


獲取HttpServletRequest / HttpSession / ServletContext / HttpServletResponse對象

方法一,通過ServletActionContext.類直接獲取:

  1. public String rsa() throws Exception{  
  2.     HttpServletRequest request = ServletActionContext.getRequest();  
  3.     ServletContext servletContext = ServletActionContext.getServletContext();  
  4.     request.getSession()      
  5.     HttpServletResponse response = ServletActionContext.getResponse();  
  6.     return "scope";  
  7. }  

方法二,實現指定接口,由struts框架運行時注入:
  1. public class HelloWorldAction implements ServletRequestAware, ServletResponseAware, ServletContextAware{  
  2.     private HttpServletRequest request;  
  3.     private ServletContext servletContext;  
  4.     private HttpServletResponse response;  
  5.     public void setServletRequest(HttpServletRequest req) {  
  6.         this.request=req;  
  7.     }  
  8.     public void setServletResponse(HttpServletResponse res) {  
  9.         this.response=res;  
  10.     }  
  11.     public void setServletContext(ServletContext ser) {  
  12.         this.servletContext=ser;  
  13.     }  
  14. }  


文件上傳

第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。這兩個文件可以從http://commons.apache.org/下載。


第二步:把form表的enctype設置爲:“multipart/form-data“,如下:

  1. <form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">  
  2.   <input  type="file" name="uploadImage">  
  3. </form>  


第三步:在Action類中添加以下屬性,屬性紅色部分對應於表單中文件字段的名稱:
  1. public class HelloWorldAction{  
  2.   private File uploadImage;//得到上傳的文件  
  3.   private String uploadImageContentType;//得到文件的類型  
  4.   private String uploadImageFileName;//得到文件的名稱  
  5.   //這裏略省了屬性的getter/setter方法  
  6.   public String upload() throws Exception{  
  7.     String realpath = ServletActionContext.getServletContext().getRealPath("/images");  
  8.     File file = new File(realpath);  
  9.     if(!file.exists()) file.mkdirs();  
  10.     FileUtils.copyFile(uploadImage, new File(file, uploadImageFileName));  
  11.     return "success";  
  12.   }  
  13. }  


多文件上傳

第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。這兩個文件可以從http://commons.apache.org/下載。

第二步:把form表的enctype設置爲:“multipart/form-data“,如下:

  1. <form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">  
  2.   <input  type="file" name="uploadImages">  
  3.   <input  type="file" name="uploadImages">  
  4. </form>  


第三步:在Action類中添加以下屬性,屬性紅色部分對應於表單中文件字段的名稱:
  1. public class HelloWorldAction {  
  2.     private File[] uploadImages;// 得到上傳的文件  
  3.     private String[] uploadImagesContentType;// 得到文件的類型  
  4.     private String[] uploadImagesFileName;// 得到文件的名稱  
  5.   
  6.     // 這裏略省了屬性的getter/setter方法  
  7.     public String upload() throws Exception {  
  8.         String realpath = ServletActionContext.getServletContext().getRealPath(  
  9.                 "/images");  
  10.         File file = new File(realpath);  
  11.         if (!file.exists())  
  12.             file.mkdirs();  
  13.         for (int i = 0; i < uploadImages.length; i++) {  
  14.             File uploadImage = uploadImages[i];  
  15.             FileUtils.copyFile(uploadImage, new File(file,  
  16.                     uploadImagesFileName[i]));  
  17.         }  
  18.         return "success";  
  19.     }  
  20. }  


自定義攔截器

要自定義攔截器需要實現com.opensymphony.xwork2.interceptor.Interceptor接口:

  1. public class PermissionInterceptor implements Interceptor {  
  2.    private static final long serialVersionUID = -5178310397732210602L;  
  3.    public void destroy() {  
  4.    }  
  5.    public void init() {  
  6.    }  
  7.    public String intercept(ActionInvocation invocation) throws Exception {  
  8.     System.out.println("進入攔截器");      
  9.     if(session裏存在用戶){  
  10.         String result = invocation.invoke();  
  11.     }else{  
  12.         return “logon”;  
  13.     }  
  14.     //System.out.println("返回值:"+ result);  
  15.     //return result;  
  16.     }  
  17. }  

  1. <package name="itcast" namespace="/test" extends="struts-default">  
  2.     <interceptors>  
  3.              <interceptor name=“permission" class="cn.itcast.aop.PermissionInterceptor" />  
  4.              <interceptor-stack name="permissionStack">  
  5.          <interceptor-ref name="defaultStack" />  
  6.         <interceptor-ref name=" permission " />  
  7.               </interceptor-stack>  
  8.     </interceptors>  
  9.     <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">  
  10.         <result name="success">/WEB-INF/page/hello.jsp</result>  
  11.         <interceptor-ref name="permissionStack"/>  
  12.     </action>  
  13. </package>  

因爲struts2中如文件上傳,數據驗證,封裝請求參數到action等功能都是由系統默認的defaultStack中的攔截器實現的,所以我們定義的攔截器需要引用系統默認的defaultStack,這樣應用纔可以使用struts2框架提供的衆多功能。
如果希望包下的所有action都使用自定義的攔截器,可以通過<default-interceptor-ref name=“permissionStack”/>把攔截器定義爲默認攔截器。注意:每個包只能指定一個默認攔截器。另外,一旦我們爲該包中的某個action顯式指定了某個攔截器,則默認攔截器不會起作用。


輸入校驗

在struts2中,我們可以實現對action的所有方法進行校驗或者對action的指定方法進行校驗。

對於輸入校驗struts2提供了兩種實現方法:
1. 採用手工編寫代碼實現。
2. 基於XML配置方式實現。


手工編寫代碼實現對action中所有方法輸入校驗

通過重寫validate() 方法實現, validate()方法會校驗action中所有與execute方法簽名相同的方法。當某個數據校驗失敗時,我們應該調用addFieldError()方法往系統的fieldErrors添加校驗失敗信息(爲了使用addFieldError()方法,action可以繼承ActionSupport ),如果系統的fieldErrors包含失敗信息,struts2會將請求轉發到名爲input的result。在input視圖中可以通過<s:fielderror/>顯示失敗信息。
validate()使用例子:

  1. public void validate() {  
  2.        if(this.mobile==null || "".equals(this.mobile.trim())){  this.addFieldError("username", "手機號不能爲空");  
  3.         }else{  if(!Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matches()){  
  4.         this.addFieldError(“mobile", "手機號的格式不正確"); }  
  5.        }  
  6. }  

驗證失敗後,請求轉發至input視圖:
  1. <result name="input">/WEB-INF/page/addUser.jsp</result>  

在addUser.jsp頁面中使用<s:fielderror/>顯示失敗信息。


手工編寫代碼實現對action指定方法輸入校驗

通過validateXxx()方法實現, validateXxx()只會校驗action中方法名爲Xxx的方法。其中Xxx的第一個字母要大寫。當某個數據校驗失敗時,我們應該調用addFieldError()方法往系統的fieldErrors添加校驗失敗信息(爲了使用addFieldError()方法,action可以繼承ActionSupport ),如果系統的fieldErrors包含失敗信息,struts2會將請求轉發到名爲input的result。在input視圖中可以通過<s:fielderror/>顯示失敗信息。

validateXxx()方法使用例子:

  1. public String add() throws Exception{  return "success";}     
  2. public void validateAdd(){  
  3.           if(username==null && "".equals(username.trim()))  this.addFieldError("username""用戶名不能爲空");  
  4. }  

驗證失敗後,請求轉發至input視圖:
  1. <result name="input">/WEB-INF/page/addUser.jsp</result>  

在addUser.jsp頁面中使用<s:fielderror/>顯示失敗信息。


輸入校驗的流程

1。類型轉換器對請求參數執行類型轉換,並把轉換後的值賦給action中的屬性。
2。如果在執行類型轉換的過程中出現異常,系統會將異常信息保存到ActionContext,conversionError攔截器將異常信息添加到fieldErrors裏。不管類型轉換是否出現異常,都會進入第3步。
3。系統通過反射技術先調用action中的validateXxx()方法,Xxx爲方法名。
4。再調用action中的validate()方法。
5。經過上面4步,如果系統中的fieldErrors存在錯誤信息(即存放錯誤信息的集合的size大於0),系統自動將請求轉發至名稱爲input的視圖。如果系統中的fieldErrors沒有任何錯誤信息,系統將執行action中的處理方法。


基於XML配置方式實現對action的所有方法進行輸入校驗

使用基於XML配置方式實現輸入校驗時,Action也需要繼承ActionSupport,並且提供校驗文件,校驗文件和action類放在同一個包下,文件的取名格式爲:ActionClassName-validation.xml,其中ActionClassName爲action的簡單類名,-validation爲固定寫法。如果Action類爲cn.itcast.UserAction,那麼該文件的取名應爲:UserAction-validation.xml。下面是校驗文件的模版:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">   
  3. <validators>  
  4.     <field name="username">  
  5.         <field-validator type="requiredstring">  
  6.             <param name="trim">true</param>  
  7.             <message>用戶名不能爲空!</message>  
  8.         </field-validator>  
  9.     </field>  
  10. </validators>  

<field>指定action中要校驗的屬性,<field-validator>指定校驗器,上面指定的校驗器requiredstring是由系統提供的,系統提供了能滿足大部分驗證需求的校驗器,這些校驗器的定義可以在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到。
<message>爲校驗失敗後的提示信息,如果需要國際化,可以爲message指定key屬性,key的值爲資源文件中的key。
在這個校驗文件中,對action中字符串類型的username屬性進行驗證,首先要求調用trim()方法去掉空格,然後判斷用戶名是否爲空。


編寫校驗文件時,不能出現幫助信息

在編寫ActionClassName-validation.xml校驗文件時,如果出現不了幫助信息,可以按下面方式解決:
windwos->preferences->myeclipse->files and editors->xml->xmlcatalog
點“add”,在出現的窗口中的location中選“File system”,然後在xwork-2.1.2解壓目錄的src\java目錄中選擇xwork-validator-1.0.3.dtd,回到設置窗口的時候不要急着關閉窗口,應把窗口中的Key Type改爲URI 。Key改爲http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd


struts2提供的校驗器列表

系統提供的校驗器如下:
required (必填校驗器,要求field的值不能爲null)
requiredstring (必填字符串校驗器,要求field的值不能爲null,並且長度大於0,默認情況下會對字符串去前後空格)
stringlength(字符串長度校驗器,要求field的值必須在指定的範圍內,否則校驗失敗,minLength參數指定最小長度,maxLength參數指定最大長度,trim參數指定校驗field之前是否去除字符串前後的空格)
regex(正則表達式校驗器,檢查被校驗的field是否匹配一個正則表達式.expression參數指定正則表達式,caseSensitive參數指定進行正則表達式匹配時,是否區分大小寫,默認值爲true)
int(整數校驗器,要求field的整數值必須在指定範圍內,min指定最小值,max指定最大值)
double(雙精度浮點數校驗器,要求field的雙精度浮點數必須在指定範圍內,min指定最小值,max指定最大值)
fieldexpression(字段OGNL表達式校驗器,要求field滿足一個ognl表達式,expression參數指定ognl表達式,該邏輯表達式基於ValueStack進行求值,返回true時校驗通過,否則不通過)
email(郵件地址校驗器,要求如果field的值非空,則必須是合法的郵件地址)
url(網址校驗器,要求如果field的值非空,則必須是合法的url地址)
date(日期校驗器,要求field的日期值必須在指定範圍內,min指定最小值,max指定最大值)
conversion(轉換校驗器,指定在類型轉換失敗時,提示的錯誤信息)
visitor(用於校驗action中的複合屬性,它指定一個校驗文件用於校驗複合屬性中的屬性)
expression(OGNL表達式校驗器,expression參數指定ognl表達式,該邏輯表達式基於ValueStack進行求值,返回true時校驗通過,否則不通過,該校驗器不可用在字段校驗器風格的配置中)


校驗器的使用例子

required  必填校驗器

  1. <field-validator type="required">  
  2.        <message>性別不能爲空!</message>  
  3. </field-validator>  

requiredstring  必填字符串校驗器
  1. <field-validator type="requiredstring">  
  2.        <param name="trim">true</param>  
  3.        <message>用戶名不能爲空!</message>  
  4. </field-validator>  

stringlength:字符串長度校驗器
  1. <field-validator type="stringlength">  
  2.     <param name="maxLength">10</param>  
  3.     <param name="minLength">2</param>  
  4.     <param name="trim">true</param>  
  5.     <message><![CDATA[產品名稱應在2-10個字符之間]]></message>  
  6. </field-validator>  

email:郵件地址校驗器

  1. <field-validator type="email">  
  2.     <message>電子郵件地址無效</message>  
  3. </field-validator>  

regex:正則表達式校驗器
  1. <field-validator type="regex">  
  2.      <param name="expression"><![CDATA[^1[358]\d{9}$]]></param>  
  3.      <message>手機號格式不正確!</message>  
  4. </field-validator>  

int:整數校驗器

  1. <field-validator type="int">  
  2.     <param name="min">1</param>  
  3.     <param name="max">150</param>  
  4.     <message>年齡必須在1-150之間</message>  
  5. </field-validator>  

字段OGNL表達式校驗器
  1. <field name="imagefile">  
  2.     <field-validator type="fieldexpression">  
  3.         <param name="expression"><![CDATA[imagefile.length() <= 0]]></param>  
  4.         <message>文件不能爲空</message>  
  5.     </field-validator>  
  6. </field>  


基於XML配置方式對指定action方法實現輸入校驗

當校驗文件的取名爲ActionClassName-validation.xml時,會對 action中的所有處理方法實施輸入驗證。如果你只需要對action中的某個action方法實施校驗,那麼,校驗文件的取名應爲:ActionClassName-ActionName-validation.xml,其中ActionName爲struts.xml中action的名稱。例如:在實際應用中,常有以下配置:

  1. <action name="user_*" class="cn.itcast.action.UserAction" method="{1}“ >  
  2.     <result name="success">/WEB-INF/page/message.jsp</result>  
  3.     <result name="input">/WEB-INF/page/addUser.jsp</result>  
  4. </action>  

UserAction中有以下兩個處理方法:
  1. public String add() throws Exception{  
  2.    ....  
  3. }  
  4. public String update() throws Exception{  
  5.    ....  
  6. }  

要對add()方法實施驗證,校驗文件的取名爲: UserAction-user_add-validation.xml
要對update()方法實施驗證,校驗文件的取名爲: UserAction-user_update-validation.xml


基於XML校驗的一些特點

當爲某個action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml兩種規則的校驗文件時,系統按下面順序尋找校驗文件:
1。AconClassName-validation.xml
2。ActionClassName-ActionName-validation.xml
系統尋找到第一個校驗文件時還會繼續搜索後面的校驗文件,當搜索到所有校驗文件時,會把校驗文件裏的所有校驗規則彙總,然後全部應用於action方法的校驗。如果兩個校驗文件中指定的校驗規則衝突,則只使用後面文件中的校驗規則。

當action繼承了另一個action,父類action的校驗文件會先被搜索到。

假設UserAction繼承BaseAction:

  1. <action name="user" class="cn.itcast.action.UserAction" method="{1}">  
  2. </action>  

訪問上面action,系統先搜索父類的校驗文件:BaseAction-validation.xml, BaseAction-user-validation.xml,接着搜索子類的校驗文件: UserAction-validation.xml, UserAction-user-validation.xml。應用於上面action的校驗規則爲這四個文件的總和。


國際化

準備資源文件,資源文件的命名格式如下:
baseName_language_country.properties
baseName_language.properties
baseName.properties
其中baseName是資源文件的基本名,我們可以自定義,但language和country必須是java支持的語言和國家。如:
中國大陸: baseName_zh_CN.properties
美國: baseName_en_US.properties

現在爲應用添加兩個資源文件:
第一個存放中文:itcast_zh_CN.properties
內容爲:welcome=歡迎來到傳智播客
第二個存放英語(美國): itcast_en_US.properties
內容爲: welcome=welcome to itcast

對於中文的屬性文件,我們編寫好後,應該使用jdk提供的native2ascii命令把文件轉換爲unicode編碼的文件。命令的使用方式如下:
native2ascii  源文件.properties  目標文件.properties


配置全局資源與輸出國際化信息

當準備好資源文件之後,我們可以在struts.xml中通過struts.custom.i18n.resources常量把資源文件定義爲全局資源文件,如下:

  1. <constant name="struts.custom.i18n.resources" value="itcast" />  
itcast爲資源文件的基本名。

後面我們就可以在頁面或在action中訪問國際化信息:
在JSP頁面中使用<s:text name=“”/>標籤輸出國際化信息:
<s:text name=“user”/>,name爲資源文件中的key
在Action類中,可以繼承ActionSupport,使用getText()方法得到國際化信息,該方法的第一個參數用於指定資源文件中的key。
在表單標籤中,通過key屬性指定資源文件中的key,如:
  1. <s:textfield name="realname" key="user"/>  


國際化—輸出帶佔位符的國際化信息

資源文件中的內容如下:
welcome= {0},歡迎來到傳智播客{1}

在jsp頁面中輸出帶佔位符的國際化信息

  1. <s:text name="welcome">  
  2.      <s:param><s:property value="realname"/></s:param>  
  3.     <s:param>學習</s:param>  
  4. </s:text>  

在Action類中獲取帶佔位符的國際化信息,可以使用getText(String key, String[] args)或getText(String aTextName, List args)方法。


國際化—包範圍資源文件

在一個大型應用中,整個應用有大量的內容需要實現國際化,如果我們把國際化的內容都放置在全局資源屬性文件中,顯然會導致資源文件變的過於龐大、臃腫,不便於維護,這個時候我們可以針對不同模塊,使用包範圍來組織國際化文件。
方法如下:
在java的包下放置package_language_country.properties資源文件,package爲固定寫法,處於該包及子包下的action都可以訪問該資源。當查找指定key的消息時,系統會先從package資源文件查找,當找不到對應的key時,纔會從常量struts.custom.i18n.resources指定的資源文件中尋找。


國際化—Action範圍資源文件

我們也可以爲某個action單獨指定資源文件,方法如下:
在Action類所在的路徑,放置ActionClassName_language_country.properties資源文件,ActionClassName爲action類的簡單名稱。
當查找指定key的消息時,系統會先從ActionClassName_language_country.properties資源文件查找,如果沒有找到對應的key,然後沿着當前包往上查找基本名爲package 的資源文件,一直找到最頂層包。如果還沒有找到對應的key,最後會從常量struts.custom.i18n.resources指定的資源文件中尋找。


國際化—JSP中直接訪問某個資源文件

struts2爲我們提供了<s:i18n>標籤,使用<s:i18n>標籤我們可以在類路徑下直接從某個資源文件中獲取國際化數據,而無需任何配置:

  1. <s:i18n name="itcast">  
  2.     <s:text name=“welcome”/>  
  3. </s:i18n>  
Itcast爲類路徑下資源文件的基本名。

如果要訪問的資源文件在類路徑的某個包下,可以這樣訪問:
  1. <s:i18n name=“cn/itcast/action/package">  
  2.     <s:text name="welcome">  
  3.         <s:param>小張</s:param>  
  4.     </s:text>  
  5. </s:i18n>  
上面訪問cn.itcast.action包下基本名爲package的資源文件。


OGNL表達式語言

OGNL是Object Graphic Navigation Language(對象圖導航語言)的縮寫,它是一個開源項目。 Struts 2框架使用OGNL作爲默認的表達式語言。
相對EL表達式,它提供了平時我們需要的一些功能,如:
支持對象方法調用,如xxx.sayHello(); 
支持類靜態方法調用和值訪問,表達式的格式爲@[類全名(包括包路徑)]@[方法名 |  值名],例如:@java.lang.String@format('foo %s', 'bar')或@cn.itcast.Constant@APP_NAME; 
操作集合對象。
Ognl 有一個上下文(Context)概念,說白了上下文就是一個MAP結構,它實現了java.utils.Map接口,在Struts2中上下文(Context)的實現爲ActionContext,下面是上下文(Context)的結構示意圖


Struts 2中的OGNL Context實現者爲ActionContext,它結構示意圖如下:

當Struts2接受一個請求時,會迅速創建ActionContext,ValueStack,action 。然後把action存放進ValueStack,所以action的實例變量可以被OGNL訪問。 


訪問上下文(Context)中的對象需要使用#符號標註命名空間,如#application、#session

另外OGNL會設定一個根對象(root對象),在Struts2中根對象就是ValueStack(值棧) 。如果要訪問根對象(即ValueStack)中對象的屬性,則可以省略#命名空間,直接訪問該對象的屬性即可。

在struts2中,根對象ValueStack的實現類爲OgnlValueStack,該對象不是我們想像的只存放單個值,而是存放一組對象。在OgnlValueStack類裏有一個List類型的root變量,就是使用他存放一組對象
 |--request   
 |--application   
context ------|--OgnlValueStack root變量[action, OgnlUtil, ... ]   
 |--session   
 |--attr   
 |--parameters

在root變量中處於第一位的對象叫棧頂對象。通常我們在OGNL表達式裏直接寫上屬性的名稱即可訪問root變量裏對象的屬性,搜索順序是從棧頂對象開始尋找,如果棧頂對象不存在該屬性,就會從第二個對象尋找,如果沒有找到就從第三個對象尋找,依次往下訪問,直到找到爲止。 
大家注意: Struts2中,OGNL表達式需要配合Struts標籤纔可以使用。如:<s:property value="name"/>

由於ValueStack(值棧)是Struts 2中OGNL的根對象,如果用戶需要訪問值棧中的對象,在JSP頁面可以直接通過下面的EL表達式訪問ValueStack(值棧)中對象的屬性:
${foo} //獲得值棧中某個對象的foo屬性
如果訪問其他Context中的對象,由於他們不是根對象,所以在訪問時,需要添加#前綴。
application對象:用於訪問ServletContext,例如#application.userName或者#application['userName'],相當於調用ServletContext的getAttribute("username")。
session對象:用來訪問HttpSession,例如#session.userName或者#session['userName'],相當於調用session.getAttribute("userName")。
request對象:用來訪問HttpServletRequest屬性(attribute)的Map,例如#request.userName或者#request['userName'],相當於調用request.getAttribute("userName")。
parameters對象:用於訪問HTTP的請求參數,例如#parameters.userName或者#parameters['userName'],相當於調用request.getParameter("username")。
attr對象:用於按page->request->session->application順序訪問其屬性。


爲何使用EL表達式能夠訪問valueStack中對象的屬性

原因是Struts2對HttpServletRequest作了進一步的封裝。簡略代碼如下:

  1. public class StrutsRequestWrapper extends HttpServletRequestWrapper {  
  2.       public StrutsRequestWrapper(HttpServletRequest req) {  
  3.           super(req);  
  4.       }  
  5.       public Object getAttribute(String s) {  
  6.        ......  
  7.        ActionContext ctx = ActionContext.getContext();  
  8.        Object attribute = super.getAttribute(s);//先從request範圍獲取屬性值  
  9.        if (ctx != null) {  
  10.            if (attribute == null) {//如果從request範圍沒有找到屬性值,即從ValueStack中查找對象的屬性值  
  11.               ......  
  12.               ValueStack stack = ctx.getValueStack();  
  13.               attribute = stack.findValue(s);  
  14.               ......  
  15.            }  
  16.        }  
  17.        return attribute;  
  18.    }  
  19. }  


採用OGNL表達式創建List/Map集合對象

如果需要一個集合元素的時候(例如List對象或者Map對象),可以使用OGNL中同集合相關的表達式。
使用如下代碼直接生成一個List對象:

  1. <s:set name="list" value="{'zhangming','xiaoi','liming'}" />  
  2. <s:iterator value="#list" id="n">  
  3.     <s:property value="n"/><br>  
  4. </s:iterator>  

生成一個Map對象:
  1. <s:set name="foobar" value="#{'foo1':'bar1', 'foo2':'bar2'}" />  
  2. <s:iterator value="#foobar" >  
  3.     <s:property value="key"/>=<s:property value="value"/><br>  
  4. </s:iterator>  

Set標籤用於將某個值放入指定範圍。
scope:指定變量被放置的範圍,該屬性可以接受application、session、request、 page或action。如果沒有設置該屬性,則默認放置在OGNL Context中。
value:賦給變量的值.如果沒有設置該屬性,則將ValueStack棧頂的值賦給變量。


採用OGNL表達式判斷對象是否存在於集合中

對於集合類型,OGNL表達式可以使用in和not in兩個元素符號。其中,in表達式用來判斷某個元素是否在指定的集合對象中;not in判斷某個元素是否不在指定的集合對象中,如下所示。
in表達式:

  1. <s:if test="'foo' in {'foo','bar'}">  
  2.    在  
  3. </s:if>  
  4. <s:else>  
  5.    不在  
  6. </s:else>  

not in表達式:
  1. <s:if test="'foo' not in {'foo','bar'}">  
  2.    不在  
  3. </s:if>  
  4. <s:else>  
  5.    在  
  6. </s:else>  

OGNL表達式的投影功能

除了in和not in之外,OGNL還允許使用某個規則獲得集合對象的子集,常用的有以下3個相關操作符。
?:獲得所有符合邏輯的元素。
^:獲得符合邏輯的第一個元素。
$:獲得符合邏輯的最後一個元素。

例如代碼:

  1. <s:iterator value="books.{?#this.price > 35}">  
  2.       <s:property value="title" /> - $<s:property value="price" /><br>  
  3. </s:iterator>  

在上面代碼中,直接在集合後緊跟.{}運算符表明用於取出該集合的子集,{}內的表達式用於獲取符合條件的元素,this指的是爲了從大集合books篩選數據到小集合,需要對大集合books進行迭代,this代表當前迭代的元素。本例的表達式用於獲取集合中價格大於35的書集合。
  1. public class BookAction extends ActionSupport {  
  2.     private List<Book> books;  
  3.     ....  
  4.     @Override  
  5.         public String execute() {  
  6.                 books = new LinkedList<Book>();  
  7.                 books.add(new Book("A735619678", "spring", 67));  
  8.         books.add(new Book("B435555322", "ejb3.0",15));  
  9.     }  
  10. }  


property標籤

property標籤用於輸出指定值:

  1. <s:set name="name" value="'kk'" />  
  2. <s:property value="#name"/>  

default:可選屬性,如果需要輸出的屬性值爲null,則顯示該屬性指定的值
escape:可選屬性,指定是否格式化HTML代碼。
value:可選屬性,指定需要輸出的屬性值,如果沒有指定該屬性,則默認輸出ValueStack棧頂的值。
id:可選屬性,指定該元素的標識

iterator標籤

iterator標籤用於對集合進行迭代,這裏的集合包含List、Set和數組。

  1. <s:set name="list" value="{'zhangming','xiaoi','liming'}" />  
  2. <s:iterator value="#list" status="st">  
  3.     <font color=<s:if test="#st.odd">red</s:if><s:else>blue</s:else>>  
  4.     <s:property /></font><br>  
  5. </s:iterator>   

value:可選屬性,指定被迭代的集合,如果沒有設置該屬性,則使用ValueStack棧頂的集合。
id:可選屬性,指定集合裏元素的id。
status:可選屬性,該屬性指定迭代時的IteratorStatus實例。該實例包含如下幾個方法:
int getCount(),返回當前迭代了幾個元素。
int getIndex(),返回當前迭代元素的索引。
boolean isEven(),返回當前被迭代元素的索引是否是偶數
boolean isOdd(),返回當前被迭代元素的索引是否是奇數
boolean isFirst(),返回當前被迭代元素是否是第一個元素。
boolean isLast(),返回當前被迭代元素是否是最後一個元素。


if/elseif/else標籤

  1. <s:set name="age" value="21" />  
  2. <s:if test="#age==23">  
  3.     23  
  4. </s:if>  
  5. <s:elseif test="#age==21">  
  6.     21  
  7. </s:elseif>  
  8. <s:else>  
  9.     都不等  
  10. </s:else>   


url標籤

  1. <s:url action="helloworld_add" namespace="/test"><s:param name="personid" value="23"/></s:url>  
生成類似如下路徑:
/struts/test/helloworld_add.action?personid=23
紅色部分爲內容路徑。

當標籤的屬性值作爲字符串類型處理時, “%”符號的用途是計算OGNL表達式的值。
  1. <s:set name="myurl" value="'http://www.foshanshop.net'"/>  
  2. <s:url value="#myurl" /><br>  
  3. <s:url value="%{#myurl}" />  

輸出結果:
#myurl
http://www.foshanshop.net


表單標籤_checkboxlist複選框

如果集合爲list

  1. <s:checkboxlist name="list" list="{'Java','.Net','RoR','PHP'}" value="{'Java','.Net'}"/>  

生成如下html代碼:
  1. <input type="checkbox" name="list" value="Java" checked="checked"/><label>Java</label>  
  2. <input type="checkbox" name="list" value=".Net" checked="checked"/><label>.Net</label>  
  3. <input type="checkbox" name="list" value="RoR"/><label>RoR</label>  
  4. <input type="checkbox" name="list" value="PHP"/><label>PHP</label>  

如果集合爲MAP
  1. <s:checkboxlist name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value" value="{1,2,3}"/>  

生成如下html代碼:
  1. <input type="checkbox" name="map" value="1" checked="checked"/><label>瑜珈用品</label>  
  2. <input type="checkbox" name="map" value="2" checked="checked"/><label>戶外用品</label>  
  3. <input type="checkbox" name="map" value="3" checked="checked"/><label>球類</label>  
  4. <input type="checkbox" name="map" value="4"/><label>自行車</label>  

如果集合裏存放的是javabean

  1.  <%  
  2.   Person person1 = new Person(1,"第一個");  
  3.   Person person2 = new Person(2,"第二個");  
  4.   List<Person> list = new ArrayList<Person>();  
  5.   list.add(person1);  
  6.   list.add(person2);  
  7.   request.setAttribute("persons",list);  
  8.   %>  
  9. <s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>  

Personid和name爲Person的屬性

生成如下html代碼:
  1. <input type="checkbox" name=“beans" value="1"/><label>第一個</label>  
  2. <input type="checkbox" name=“beans" value="2"/><label>第二個</label>  


表單標籤_radio單選框

該標籤的使用和checkboxlist複選框相同。
如果集合裏存放的是javabean(personid和name爲Person的屬性)

  1. < s:radio name="beans" list="#request.persons" listKey="personid" listValue="name"/>  

生成如下html代碼:
  1. <input type="radio" name="beans" id="beans1" value="1"/><label>第一個</label>  
  2. <input type="radio" name="beans" id="beans2" value="2"/><label>第二個</label>  

如果集合爲MAP
  1. <s:radio name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value“ value="1"/>  

生成如下html代碼:
  1. <input type="radio" name="map" id="map1" value="1"/><label for="map1">瑜珈用品</label>  
  2. <input type="radio" name="map" id="map2" value="2"/><label for="map2">戶外用品</label>  
  3. <input type="radio" name="map" id="map3" value="3"/><label for="map3">球類</label>  
  4. <input type="radio" name="map" id="map4" value="4"/><label for="map4">自行車</label>  

如果集合爲list
  1. <s:radio name="list" list="{'Java','.Net'}" value="'Java'"/>  

生成如下html代碼:
  1. <input type="radio" name="list" checked="checked" value="Java"/><label>Java</label>  
  2. <input type="radio" name="list" value=".Net"/><label>.Net</label>  


表單標籤_select下拉選擇框

  1. <s:select name="list" list="{'Java','.Net'}" value="'Java'"/>  
  2. <select name="list" id="list">  
  3.     <option value="Java" selected="selected">Java</option>  
  4.     <option value=".Net">.Net</option>  
  5. </select>  
  6. <s:select name="beans" list="#request.persons" listKey="personid" listValue="name"/>  
  7. <select name="beans" id="beans">  
  8.     <option value="1">第一個</option>  
  9.     <option value="2">第二個</option>  
  10. </select>  
  11. <s:select name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value" value="1"/>  
  12. <select name="map" id="map">  
  13.     <option value="1" selected="selected">瑜珈用品</option>  
  14.     <option value="2">戶外用品</option>  
  15.     <option value="3">球類</option>  
  16.     <option value="4">自行車</option>  
  17. </select>  

<s:token />標籤防止重複提交

<s:token />標籤防止重複提交,用法如下:
第一步:在表單中加入<s:token />

  1. <s:form action="helloworld_other" method="post" namespace="/test">  
  2.   <s:textfield name="person.name"/><s:token/><s:submit/>  
  3.   </s:form>  

第二步:
  1. <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">  
  2.        <interceptor-ref name="defaultStack" />  
  3.         <interceptor-ref name="token" />  
  4.         <result name="invalid.token">/WEB-INF/page/message.jsp</result>    
  5.         <result>/WEB-INF/page/result.jsp</result>         
  6. </action>  

以上配置加入了“token”攔截器和“invalid.token”結果,因爲“token”攔截器在會話的token與請求的token不一致時,將會直接返回“invalid.token”結果。

在debug狀態,控制檯出現下面信息,是因爲Action中並沒有struts.token和struts.token.name屬性,我們不用關心這個錯誤:
嚴重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting 'struts.token' on 'class xxx: Error setting expression 'struts.token' with value '[Ljava.lang.String;@39f16f'
嚴重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting 'struts.token.name'
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章