Struts2框架學習第二章——Struts2下的HelloWorld

本章要點html

—  Struts 2的下載和安裝java

— 純手工建立一個Web應用程序員

— 純手工建立一個Struts 2應用web

— 實現Struts 2的Action數據庫

— 配置Struts 2的Actionapache

— 在Action中訪問HttpSession編程

— 在JSP中輸出Action的返回值數組

— 使用Struts 2的表單標籤瀏覽器

— 程序國際化初步服務器

— 數據校驗初步

前面已經簡要介紹了Struts 2的起源,以及Struts 2的兩個前身:Struts 1和WebWork,並詳細對比了Struts 2和Struts 1的差別,對比了Struts 2和WebWork的差別,並且指出:Struts 2是WebWork的升級,而不是Struts 1的升級。

雖然Struts 2提供了與Struts 1的兼容,但已經不是Struts 1的升級。對於已有Struts 1開發經驗的開發者而言,Struts 1的開發經驗對於Struts 2並無太大的幫助;相反,對於已經有WebWork開發經驗的開發者而言,WebWork的開發經驗對Struts 2的開發將有很好的借鑑意義。

下面將以一個Struts 2的HelloWorld應用爲例,介紹Strust 2 MVC框架如何攔截用戶請求,如何調用業務控制器處理用戶請求,並介紹Action處理結果和資源之間的映射關係。

本HelloWorld應用是一個簡單的應用:用戶進入一個登陸頁面,容許用戶輸入用戶名、密碼,若是用戶輸入的用戶名和密碼符合要求,則進入一個歡迎頁面;若是用戶輸入錯誤,則進入一個提示頁面。當用戶提交表單時,本應用會有基本的數據校驗。

2.1  下載和安裝Struts 2框架

下面咱們從下載、安裝Struts 2開始,慢慢開始體驗Struts 2 MVC框架的魅力。

筆者寫本書的時候,Struts 2已經發布了其產品化GA(General Availability)版,其實最新的產品化GA版是Struts 2.06,故本書的全部應用都是基於該版本的Struts 2。建議讀者下載Struts 2.06版,而不是下載最新的Beta版,若是Struts 2有最新的GA版,讀者也能夠下載更新的GA版,相信不會有太大差別。

下載和安裝DWR請按以下步驟進行。

 登陸http://struts.apache.org/download.cgi#Struts206站點,下載Struts 2的最新GA版。在Struts 2.06下有以下幾個選項:

—  Full Distribution:下載Struts 2的完整版。一般建議下載該選項。

—  Example Applications:下載Struts 2的示例應用,這些示例應用對於學習Struts 2有很大的幫助,下載Struts 2的完整版時已經包含了該選項下所有應用。

—  Blank Application only:僅下載Struts 2的空示例應用,這個空應用已經包含在Example Applications選項下。

—  Essential Dependencies:僅僅下載Struts 2的核心庫,下載Struts 2的完整版時將包括該選項下的所有內容。

—  Documentation:僅僅下載Struts 2的相關文檔,包含Struts 2的使用文檔、參考手冊和API文檔等。下載Struts 2的完整版時將包括該選項下的所有內容。

—  Source:下載Struts 2的所有源代碼,下載Struts 2的完整版時將包括該選項下的所有內容。

—  Alternative Java 4 JARs:下載可選的JDK 1.4的支持JAR。下載Struts 2的完整版時將包括該選項下的所有內容。

一般建議讀者下載第一個選項:下載Struts 2的完整版,將下載到的Zip文件解壓縮,該文件就是一個典型的Web結構,該文件夾包含以下文件結構:

—  apps:該文件夾下包含了基於Struts 2的示例應用,這些示例應用對於學習者是很是有用的資料。

—  docs:該文件夾下包含了Struts 2的相關文檔,包括Struts 2的快速入門、Struts 2的文檔,以及API文檔等內容。

—  j4:該文件夾下包含了讓Struts 2支持JDK 1.4的JAR文件。

—  lib:該文件夾下包含了Struts 2框架的核心類庫,以及Struts 2的第三方插件類庫。

—  src:該文件夾下包含了Struts 2框架的所有源代碼。

 將lib文件夾下的Struts2-core-2.0.6.jar、xwork-2.0.1.jar和ognl-2.6.11.jar等必需類庫複製到Web應用的WEB-INF/lib路徑下。固然,若是你的Web應用須要使用Struts 2的更多特性,則須要將更多的JAR文件複製到Web應用的WEB-INF/lib路徑下。若是須要在DOS或者Shell窗口下手動編譯Struts 2相關的程序,則還應該將Struts2-core-2.0.6.jar和xwork-2.0.1.jar添加到系統的CLASSPATH環境變量裏。

 提示  大部分時候,使用Struts 2的Web應用並不須要利用到Struts 2的所有特性,所以沒有必要一次將該lib路徑下JAR文件所有複製到Web應用的WEB-INF/lib路徑下。

 編輯Web應用的web.xml配置文件,配置Struts 2的核心Filter。下面是增長了Struts 2的核心Filter配置的web.xml配置文件的代碼:

<?xml version="1.0" encoding="GBK"?>

<!-- web-app是Web應用配置文件的根元素,指定Web應用的Schema信息 -->

<web-app 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">

                                 <!-- 定義Struts 2的FilterDispatcher的Filter -->

                                  <filter>

                                    <!-- 定義核心Filter的名字 -->

                                  <filter-name>struts2</filter-name>

                                    <!-- 定義核心Filter的實現類 -->

                                  <filter-class>org.apache.Struts2.dispatcher.FilterDispatcher

                                        </ filter-class>

                                  </filter>

                                 <!-- FilterDispatcher用來初始化Struts 2而且處理全部的Web請求 -->

                                  <filter-mapping>

                                  <filter-name>Struts2</filter-name>

                                  <url-pattern>/*</url-pattern>

                                  </filter-mapping>

</web-app>

通過上面3個步驟,咱們已經能夠在一個Web應用中使用Struts 2的基本功能了,下面將帶領讀者進入Struts 2 MVC框架的世界。

2.2  從用戶請求開始

Struts 2支持大部分視圖技術,固然也支持最傳統的JSP視圖技術,本應用將使用最基本的視圖技術:JSP技術。當用戶須要登陸本系統時,用戶須要一個簡單的表單提交頁面,這個表單提交頁面包含了兩個表單域:用戶名和密碼。

下面是一個最簡單的表單提交頁面,該頁面的表單內僅包含兩個表單域,甚至沒有任何動態內容,實際上,整個頁面徹底能夠是一個靜態HTML頁面。但考慮到須要在該頁面後面增長動態內容,所以依然將該頁面以jsp爲後綴保存。下面是用戶請求登陸的JSP頁面代碼:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<html>

<head>

<title>登陸頁面</title>

</head>

<body>

<!-- 提交請求參數的表單 -->

<form action="Login.action" method="post">

                                  <table align="center">

                                  <caption><h3>用戶登陸</h3></caption>

                                  <tr>

                                        <!-- 用戶名的表單域 -->

                                   <td>用戶名:<input type="text" name="username"/></td>

                                  </tr>

                                  <tr>

                                        <!-- 密碼的表單域 -->

                                   <td>密&nbsp;&nbsp;碼:<input type="text" name="password"/></td>

                                  </tr>

                                  <tr align="center">

                                   <td colspan="2"><input type="submit" value="登陸"/><input

                                            type="reset" value="重填" /></td>

                                  </tr>

                                  </table>

</form>

</body>

</html>

正如前面介紹的,該頁面沒有包含任何的動態內容,徹底是一個靜態的HTML頁面。但咱們注意到該表單的action屬性:login.action,這個action屬性比較特殊,它不是一個普通的Servlet,也不是一個動態JSP頁面。可能讀者已經猜到了,當表單提交給login.action時,Struts 2的FilterDispatcher將自動起做用,將用戶請求轉發到對應的Struts 2 Action。

 注意  Struts 2 Action默認攔截全部後綴爲.action的請求。所以,若是咱們須要將某個表單提交給Struts 2 Action處理,則應該將該表單的action屬性設置爲*.action的格式。

該頁面就是一個基本的HTML頁面,在瀏覽器中瀏覽該頁面,看到如圖2.1所示的界面。

整個頁面就是一個標準的HTML頁面,整個單獨的頁面尚未任何與用戶交互的能力。下面咱們開始動手建立一個Struts 2的Web應用。

2.3  建立Struts 2Web應用

Struts 2的Web應用就是一個普通的Web應用,而後增長Struts 2功能,該應用就能夠充分利用Struts 2的MVC框架了。

2.3.1  建立Web應用

筆者一直相信:要想成爲一個優秀的程序員,應該從基本功練起,全部的代碼都應該用簡單的文本編輯器(包括EditPlus、UtraEdit等工具)完成。筆者常常見到一些有兩三年開發經驗的程序員,一旦離開了熟悉的IDE(集成開發環境,如Eclipse、JBuilder等),徹底不能動手寫任何代碼。而他們每每還振振有詞:誰會不用任何工具來開發?

實際上,真正優秀的程序員固然可使用IDE工具,但即便使用VI(UNIX下無格式編輯器)、記事本也同樣能夠完成很是優秀的項目。筆者對於IDE工具的態度是:可使用IDE工具,但毫不可依賴於IDE工具。學習階段,千萬不要使用IDE工具;開發階段,纔去使用IDE工具。

 提醒  對於IDE工具,業內有一個說法:IDE工具會加快高手的開發效率,但會使初學者更白癡。

爲了讓讀者更加清楚Struts 2應用的核心,筆者下面將「徒手」創建一個Struts 2應用。

創建一個Web應用請按以下步驟進行。

 在任意目錄新建一個文件夾,筆者將以該文件夾創建一個Web應用。

 在第1步所建的文件夾內建一個WEB-INF文件夾。

 進入Tomcat,或任何Web容器內,找到任何一個Web應用,將Web應用的WEB-INF下的web.xml文件複製到第2步所建的WEB-INF文件夾下。

 修改複製的web.xml文件,將該文件修改爲只有一個根元素的XML文件,修改後的web.xml文件代碼以下:

<?xml version="1.0" encoding="GBK"?>

<!-- web-app是Web應用配置文件的根元素,指定Web應用的Schema信息 -->

<web-app 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">

</web-app>

 在第2步所建的WEB-INF路徑下,新建兩個文件夾:classes和lib,它們分別用於保存單個*.class文件和JAR文件。

通過上面步驟,已經創建了一個空Web應用。將該Web應用複製到Tomcat的webapps路徑下,該Web應用將能夠自動部署在Tomcat中。

將2.2節所定義的JSP頁面文件複製到第1步所建的文件夾下,該JSP頁面將成爲該Web應用的一個頁面。該Web將有以下文件結構:

Struts2qs

|-WEB-INF

|     |-classes

|     |-lib

|     |-web.xml

|-login.jsp

上面的Struts2qs是Web應用所對應文件夾的名字,能夠更改;login.jsp是該Web應用下JSP頁面的名字,也能夠修改。其餘文件夾、配置文件都不能夠修改。

啓動Tomcat,在瀏覽器中瀏覽2.2節定義的JSP頁面,將看到如圖2.1所示的頁面。

2.3.2  增長Struts 2功能

爲了給Web應用增長Struts 2功能,只須要將Struts 2安裝到Web應用中便可。在Web應用中安裝Struts 2框架核心只須要通過以下三個步驟。

 修改web.xml文件,在web.xml文件中配置Struts 2的核心Filter。

 將Struts 2框架的類庫複製到Web應用的WEB-INF/lib路徑下。

 在WEB-INF/classes下增長struts.xml配置文件。

下面是增長了Struts 2功能後Web應用的文件結構:

Struts2qs

|-WEB-INF

|     |-classes(struts.xml)

|     |-lib(commons-logging.jar,freemarker.jar,ognl.jar,struts2-core.jar,xwork.jar)

|     |-web.xml

|-login.jsp

在上面的文件結構中,lib下Struts 2框架的類庫可能有版本後綴。例如commons-logging.jar,多是commons-logging-1.1.jar;struts2-core.jar多是struts2-core-2.0.6.jar。

修改後的web.xml文件在2.1節已經給出了,故此處再也不贅述。

此處須要給讀者指出的是,Struts 2的Web應用默認須要Java 5運行環境,須要Web容器支持Servlet API 2.4和JSP API 2.0。若是讀者須要使用更低版本的Java運行時環境,則須要使用Struts 2框架的JDK 1.4支持。爲了簡單起見,筆者建議讀者使用Java 5運行時環境,使用Tomcat 5.5或者更高版本。

 注意  Struts 2應用默認須要Java 5運行時環境,須要支持Servlet API 2.4和JSP API 2.0的Web容器。

2.4  實現控制器

前面介紹MVC框架時,已經指出:MVC框架的核心就是控制器。當用戶經過2.2節的頁面提交用戶請求時,該請求須要提交給Struts 2的控制器處理。Struts 2的控制器根據處理結果,決定將哪一個頁面呈現給客戶端。

2.4.1  實現控制器類

Struts 2下的控制器再也不像Struts 1下的控制器,須要繼承一個Action父類,甚至能夠無需實現任何接口,Struts 2的控制器就是一個普通的POJO。

實際上,Struts 2的Action就是一個包含execute方法的普通Java類,該類裏包含的多個屬性用於封裝用戶的請求參數。下面是處理用戶請求的Action類的代碼:

//Struts 2的Action類就是一個普通的Java類

public class LoginAction

{

                                 //下面是Action內用於封裝用戶請求參數的兩個屬性

                                  private String username;

                                  private String password;

                                  //username屬性對應的getter方法

                                  public String getUsername()

                                 {

                                  return username;

                                  }

                                 //username屬性對應的setter方法

                                  public void setUsername(String username)

                                  {

                                  this.username = username;

                                  }

                                  //password屬性對應的getter方法

                                  public String getPassword()

                                 {

                                  return password;

                                  }

                                 //password屬性對應的setter方法

                                  public void setPassword(String password)

                                 {

                                  this.password = password;

                                  }

                                 //處理用戶請求的execute方法

                                 public String execute() throws Exception

                                 {

                                    //當用戶請求參數的username等於scott,密碼請求參數爲tiger時,返回success

                                      字符串

                                    //不然返回error字符串

                                  if (getUsername().equals("scott")

                                        && getPassword().equals("tiger") )

                                    {

                                   return "success";

                                  }

                                    else

                                    {

                                   return "error";

                                  }

                                  }

}

上面的Action類是一個再普通不過的Java類,該類裏定義了兩個屬性:username和password,併爲這兩個屬性提供了對應的setter和getter方法。除此以外,該Action類裏還包含了一個無參數的execute方法——這大概也是Action類與POJO惟一的差異。實際上,這個execute方法依然是一個很普通的方法,既沒有與Servlet API耦合,也沒有與Struts 2 API耦合。

 提示  表面上看起來,該Action的兩個屬性只提供了對應的setter和getter方法,很難理解請求參數在何時賦值給該Action的屬性,事實上,由於Struts 2的攔截器機制,它們負責解析用戶的請求參數,並將請求參數賦值給Action對應的屬性。

2.4.2  配置Action

上面定義了Struts 2的Action,但該Action還未配置在Web應用中,還不能處理用戶請求。爲了讓該Action能處理用戶請求,還須要將該Action配置在struts.xml文件中。

前面已經介紹過了,struts.xml文件應該放在classes路徑下,該文件主要放置Struts 2的Action定義。定義Struts 2 Action時,除了須要指定該Action的實現類外,還須要定義Action處理結果和資源之間的映射關係。下面是該應用的struts.xml文件的代碼:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Struts 2配置文件的DTD信息 -->

<!DOCTYPE struts PUBLIC

        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

        "http://struts.apache.org/dtds/struts-2.0.dtd">

<!-- struts是Struts 2配置文件的根元素 -->

<struts>

                                 <!-- Struts 2的Action必須放在指定的包空間下定義 -->

                                 <package name="strutsqs" extends="struts-default">

                                    <!-- 定義login的Action,該Action的實現類爲lee.Action類 -->

                                  <action name="Login" class="lee.LoginAction">

                                        <!-- 定義處理結果和資源之間映射關係。 -->

                                   <result name="error">/error.jsp</result>

                                   <result name="success">/welcome.jsp</result>       

                                  </action>

                                  </package>

</struts>

上面映射文件定義了name爲login的Action,即:該Action將負責處理向login.action URL請求的客戶端請求。該Action將調用自身的execute方法處理用戶請求,若是execute方法返回success字符串,請求將被轉發到/welcome.jsp頁面;若是execute方法返回error字符串,則請求被轉發到/error.jsp頁面。

2.4.3  增長視圖資源完成應用

通過上面步驟,這個最簡單的Struts 2應用幾乎能夠運行了,但還須要爲該Web應用增長兩個JSP文件,兩個JSP文件分別是error.jsp頁面和welcome.jsp頁面,將這兩個JSP頁面文件放在Web應用的根路徑下(與WEB-INF在同一個文件夾下)。

這兩個JSP頁面文件是更簡單的頁面,它們只是包含了簡單的提示信息。其中welcome.jsp頁面的代碼以下:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<html>

    <head>

        <title>成功頁面</title>

    </head>

    <body>

        您已經登陸!

    </body>

</html>

上面的頁面就是一個普通的HTML頁面,登陸失敗後進入的error.jsp頁面也與此徹底相似。

在如圖2.1所示頁面的「用戶名」輸入框中輸入scott,在「密碼」輸入框中輸入tiger,頁面將進入welcome.jsp頁面,將看到如圖2.2所示的頁面。

對於上面的處理流程,能夠簡化爲以下的流程:用戶輸入兩個參數,即username和password,而後向login.action發送請求,該請求被FilterDispatcher轉發給LoginAction處理,若是LoginAction處理用戶請求返回success字符串,則返回給用戶welcome.jsp頁面;若是返回error字符串,則返回給用戶error.jsp頁面。

圖2.3顯示了上面應用的處理流程。

 

圖2.3  HelloWorld應用的處理流程

2.5  改進控制器

經過前面介紹,讀者已經能夠完成簡單的Struts 2的基本應用了,但還能夠進一步改進應用的Action類,例如該Action類能夠經過實現Action接口,利用該接口的優點。前面應用的Action類沒有與JavaBean交互,沒有將業務邏輯操做的結果顯示給客戶端。

2.5.1  實現Action接口

表面上看起來,實現Struts 2的Action接口沒有太大的好處,僅會污染該Action的實現類。事實上,實現Action接口能夠幫助開發者更好地實現Action類。下面首先看Action接口的定義:

public interface Action

{

                                 //下面定義了5個字符串常量

                                  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";

                                 //定義處理用戶請求的execute抽象方法

                                  public String execute() throws Exception;

}

在上面的Action代碼中,咱們發現該Action接口裏已經定義了5個標準字符串常量:SUCCESS、NONE、ERROR、INPUT和LOGIN,它們能夠簡化execute方法的返回值,並可使用execute方法的返回值標準化。例如對於處理成功,則返回SUCCESS常量,避免直接返回一個success字符串(程序中應該儘可能避免直接返回數字常量、字符串常量等)。

所以,藉助於上面的Action接口,咱們能夠將原來的Action類代碼修改成以下:

//實現Action接口來實現Struts 2的Action類

public class LoginAction implements Action

{

                                 //下面是Action內用於封裝用戶請求參數的兩個屬性

                                  private String username;

                                 private String password;

                                 //username屬性對應的getter方法

                                  public String getUsername()

                                 {

                                  return username;

                                  }

                                 //username屬性對應的setter方法

                                  public void setUsername(String username)

                                 {

                                  this.username = username;

                                 }

                                 //password屬性對應的getter方法

                                  public String getPassword()

                                 {

                                  return password;

                                  }

                                 //password屬性對應的setter方法

                                  public void setPassword(String password)

                                 {

                                  this.password = password;

                                  }

                                 //處理用戶請求的execute方法

                                 public String execute() throws Exception

                                 {

                                    //當用戶請求參數的username等於scott,密碼請求參數爲tiger時,返回success

                                      字符串

                                    //不然返回error的字符串

                                  if (getUsername().equals("scott")

                                        && getPassword().equals("tiger") )

                                    {

                                   return SUCCESS;

                                  }

                                    else

                                    {

                                   return ERROR;

                                  }

                                  }

}

對比前面Action和此處的Action實現類,咱們發現兩個Action類的代碼基本類似,除了後面的Action類實現了Action接口。由於實現了Action接口,故Action類的execute方法能夠返回Action接口裏的字符串常量。

2.5.2  跟蹤用戶狀態

前面的Action處理完用戶登陸後,僅僅執行了簡單的頁面轉發,並未跟蹤用戶狀態信息——一般,當一個用戶登陸成功後,須要將用戶的用戶名添加爲Session狀態信息。

爲了訪問HttpSession實例,Struts 2提供了一個ActionContext類,該類提供了一個getSession的方法,但該方法的返回值類型並非HttpSession,而是Map。這又是怎麼回事呢?實際上,這與Struts 2的設計哲學有關,Struts 2爲了簡化Action類的測試,將Action類與Servlet API徹底分離,所以getSession方法的返回值類型是Map,而不是HttpSession。

雖然ActionContext的getSession返回的不是HttpSession對象,但Struts 2的系列攔截器會負責該Session和HttpSession之間的轉換。

爲了能夠跟蹤用戶信息,咱們修改Action類的execute方法,在execute方法中經過ActionContext訪問Web應用的Session。修改後的execute方法代碼以下:

//處理用戶請求的execute方法

public String execute() throws Exception

{

                                 //當用戶請求參數的username等於scott,密碼請求參數爲tiger時,返回success字符串

                                 //不然返回error的字符串

                                  if (getUsername().equals("scott")

                                    && getPassword().equals("tiger") )

                                 {

                                    //經過ActionContext對象訪問Web應用的Session

                                    ActionContext.getContext().getSession().put("user" , getUsername());

                                  return SUCCESS;

                                  }

                                 else

                                 {

                                  return ERROR;

                                  }

}

上面的代碼僅提供了Action類的execute方法,該Action類的其餘部分與前面的Action類代碼徹底同樣。在上面的Action類經過ActionContext設置了一個Session屬性:user。爲了檢驗咱們設置的Session屬性是否成功,咱們修改welcome.jsp頁面,在welcome.jsp頁面中使用JSP 2.0表達式語法輸出Session中的user屬性。下面是修改後的welcome.jsp頁面代碼:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<html>

    <head>

        <title>成功頁面</title>

    </head>

    <body>

        歡迎,${sessionScope.user},您已經登陸!

    </body>

</html>

上面的JSP頁面與前面的JSP頁面沒有太大改變,除了使用了JSP 2.0語法來輸出Session中的user屬性。關於JSP 2.0表達式的知識,請參看筆者所著的《輕量級J2EE企業應用實戰》一書的第2章。

在如圖2.1所示頁面的「用戶名」輸入框中輸入scott,在「密碼」輸入框中輸入tiger,而後單擊「登陸」按鈕,將看到如圖2.4所示的頁面。

在上面登陸成功的頁面中,已經輸出登陸所用的用戶名:scott,可見在Action經過ActionContext設置Session是成功的。

2.5.3  添加處理信息

到目前爲止,Action僅僅控制轉發用戶請求,JSP頁面並未得到Action的處理結果。對於大部分Web應用而言,用戶須要得到請求Action的處理結果,例如,在線購物系統須要查詢某個種類下的商品,則Action調用業務邏輯組件的業務邏輯方法獲得該種類下的所有商品,而JSP頁面則獲取該Action的處理結果,並將所有結果迭代輸出。

下面將爲應用增長一個Action,該Action負責獲取某個系列的所有書籍。爲了讓該Action能夠獲取這系列的書籍,咱們增長一個業務邏輯組件,它包含一個業務邏輯方法,該方法能夠獲取某個系列的所有書籍。

下面是系統所用的業務邏輯組件的代碼:

public class BookService

{

                                 //以一個常量數組模擬了從持久存儲設備(數據庫)中取出的數據

                                 private String[] books =

                                    new String[]{

                                    "Spring2.0寶典" ,

                                    "輕量級J2EE企業應用實戰",

                                    "基於J2EE的Ajax寶典",

                                    "Struts,Spring,Hibernate整合開發"

                                 };

                                 //業務邏輯方法,該方法返回所有圖書

                                 public String[] getLeeBooks()

                                 {

                                    return books;

                                 }

}

上面的業務邏輯組件實際上就是MVC模式中的Model,它負責實現系統業務邏輯方法。理論上,業務邏輯組件實現業務邏輯方法時,必須依賴於底層的持久層組件,但此處的業務邏輯組件則只是返回一個靜態的字符串數組——由於這只是一種模擬。

 注意  此處的業務邏輯組件只是模擬實現業務邏輯方法,並未真正調用持久層組件來獲取數據庫信息。

在系統中增長以下Action類,該Action類先判斷Session中user屬性是否存在,而且等於scott字符串——這要求查看圖書以前,用戶必須已經登陸本系統。若是用戶已經登陸本系統,則獲取系統中所有書籍,不然返回登陸頁面。

新增的Action類的代碼以下:

public class GetBooksAction implements Action

{

                                 //該屬性並不用於封裝用戶請求參數,而用於封裝Action須要輸出到JSP頁面信息

                                 private String[] books;

                                 //books屬性的setter方法

                                 public void setBooks(String[] books)

                                 {

                                    this.books = books;

                                 }

                                 //books屬性的getter方法

                                 public String[] getBooks()

                                 {

                                    return books;

                                 }

                                 //處理用戶請求的execute方法

                                 public String execute() throws Exception

                                 {

                                    //獲取Session中的user屬性

                                    String user = (String)ActionContext.getContext().getSession().

                                        get("user");

                                    //若是user屬性不爲空,且該屬性值爲scott

                                    if (user != null && user.equals("scott"))

                                    {

                                        //建立BookService實例

                                        BookService bs = new BookService();

                                        //將業務邏輯組件的返回值設置成該Action的屬性

                                        setBooks(bs.getLeeBooks());

                                        return SUCCESS;

                                    }

                                    else

                                    {

                                        return LOGIN;

                                    }

    }

}

經過上面的Action類,咱們發現Action類中的成員屬性,並不必定用於封裝用戶的請求參數,也多是封裝了Action須要傳入下一個JSP頁面中顯示的屬性。

 提示  Action中的成員屬性,並必定用於封裝用戶的請求參數,也多是封裝了Action須要傳入下一個頁面顯示的值。實際上,這些值將被封裝在ValueStack對象中。

當咱們的控制器須要調用業務邏輯方法時,咱們直接建立了一個業務邏輯組件的實例,這並非一種好的作法,由於控制器不該該關心業務邏輯組件的實例化過程。比較成熟的作法能夠利用工廠模式來管理業務邏輯組件;固然,目前最流行的方式是利用依賴注入——這將在後面章節裏介紹。

 注意  實際項目中不會在控制器中直接建立業務邏輯組件的實例,而是經過工廠模式管理業務邏輯組件實例,或者經過依賴注入將業務邏輯組件實例注入控制器組件。

該Action處理用戶請求時,無需得到用戶的任何請求參數。將該Action配置在struts.xml文件中,配置該Action的配置片斷以下:

<!-- 定義獲取系統中圖書的Action,對應實現類爲lee.GetBooksAction -->

<action name="GetBooks" class="lee.GetBooksAction">

                                 <!-- 若是處理結果返回login,進入login.jsp頁面 -->

                                 <result name="login">/login.jsp</result>

                                 <!-- 若是處理結果返回success,進入showBook.jsp頁面 -->

                                  <result name="success">/showBook.jsp</result>

</action>

當用戶向getBooks.action發送請求時,該請求將被轉發給lee.GetBooksAction處理。

2.5.4  輸出處理信息

若是用戶沒有登陸,直接向getBooks.action發送請求,該請求將被轉發到login.jsp頁面。若是用戶已經登陸,getBooks.action將從系統中加載到系統中的全部圖書,並將請求轉發給showBook.jsp頁面,所以showBook.jsp頁面必須負責輸出所有圖書。

下面筆者將以最原始的方式:JSP腳原本輸出所有圖書。

 注意  在實際應用中,幾乎絕對不會使用筆者這種方式來輸出Action轉發給JSP輸出的信息,但筆者爲了讓讀者更清楚Struts 2標籤庫在底層所完成的動做,故此處使用JSP腳原本輸出所有圖書信息。

當Action設置了某個屬性值後,Struts 2將這些屬性值所有封裝在一個叫作struts.valueStack的請求屬性裏。

 提示  讀者可能感到奇怪:筆者是如何知道Struts 2將這些屬性值封裝在struts.valueStack請求屬性裏的?這一方面與編程經驗有關,另外一方面能夠經過查看Struts 2的各類文檔,最重要的一點是能夠在showBook.jsp頁面中經過getAttributeNames方法分析請求中的所有屬性。

爲了在JSP頁面中輸出須要輸出的圖書信息,咱們能夠經過以下代碼來獲取包含所有輸出信息的ValueStack對象。

//獲取封裝輸出信息的ValueStack對象

request.getAttribute("struts.valueStack");

上面代碼返回一個ValueStack對象,該對象封裝了所有的輸出信息。該對象是 Struts 2使用的一個ValueStack對象,能夠經過OGNL表達式很是方便地訪問該對象封裝的信息。

從數據結構上來看,ValueStack有點相似於Map結構,但它比Map結構更增強大(由於它能夠根據表達式來查詢值)。Action全部的屬性都被封裝到了ValueStack對象中,Action中的屬性名能夠理解爲ValueStack中value的名字。

大體理解了ValueStack對象的結構後,咱們能夠經過以下代碼來獲取Action中設置的所有圖書信息。

//調用ValueStack的fineValue方法查看某個表達式的值

vs.findValue("books");

理解了上面關鍵的兩步,整個JSP頁面的代碼就比較容易瞭解了。下面是showBook.jsp頁面的代碼:

<%@ page language="java" contentType="text/html; charset=GBK">

<% @page import="java.util.*,com.opensymphony.xwork2.util.*"%>

<html>

    <head>

        <title>做者李剛的圖書</title>

    </head>

    <body>

                                 <table border="1" width="360">

                                 <caption>做者李剛的圖書</caption>

                                 <%

                                 //獲取封裝輸出信息的ValueStack對象

                                 ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");

                                 //調用ValueStack的fineValue方法獲取Action中的books屬性值

                                 String[] books = (String[])vs.findValue("books");

                                 //迭代輸出所有圖書信息

                                 for (String book : books)

                                 {

                                 %>

                                 <tr>

                                    <td>書名:</td>

                                    <td><%=book%></td>

                                 </tr>

                                 <%}%>

                                 </table>

    </body>

</html>

不能否認,上面JSP頁面的代碼是醜陋的,並且難以維護,由於裏面鑲嵌了大量的Java腳本。但它對於讀者理解Struts 2如何處理封裝在Action的ValueStack卻頗有幫助。

在瀏覽器中向getBooks.action發送請求,將看到如圖2.5所示的頁面。

經過上面頁面,咱們看到JSP頁面已經輸出了Struts 2控制器的返回信息。上面整個過程,已經徹底包括了Struts 2框架的3個部分:視圖、控制器和模型。

 

圖2.5  在JSP頁面中輸出Action的返回信息

2.6  改進視圖組件

經過前面的幾節的介紹,咱們已經明白了Struts 2 MVC框架的基本數據流,已經完成了Struts 2應用中模型、控制器、視圖3個組件的開發。但應用中的視圖組件:JSP頁面很是醜陋,特別是輸出Action返回信息的JSP頁面,使用了大量的Java腳原本控制輸出,下面將會使用Struts 2的標籤來改善整個應用視圖組件。

2.6.1  改善輸出頁面

爲了控制輸出Struts 2的ValueStack中封裝的值,Struts 2提供了大量的標籤。其中比較經常使用的標籤有:

—  if:該標籤支持標籤體,若是if標籤裏判斷的表達式返回真,則輸出標籤體內容。

—  else:該標籤不能獨立使用,它須要與if標籤結合使用,若是if標籤內判斷的表達式返回假,則輸出該標籤裏的標籤體。

—  iterator:主要用於迭代輸出某個集合屬性的各個集合元素。

—  property:該標籤用於輸出指定屬性值。

關於Struts 2標籤庫更深刻的使用,第10章還會深刻介紹,故此處再也不詳細講解。經過使用上面的幾個標籤,替換showBook.jsp頁面中的Java腳本,修改後的showBook.jsp頁面代碼以下:

<%@ page language="java" contentType="text/html; charset=GBK" %>

<!-- 導入Struts 2的標籤庫 -->

<%@taglib prefix="s" uri="/struts-tags"%>

<html>

    <head>

        <title>做者李剛的圖書</title>

    </head>

    <body>

                                 <table border="1" width="360">

                                 <caption>做者李剛的圖書</caption>

                                 <!-- 迭代輸出ValueStack中的books對象,其中status是迭代的序號 -->

                                 <s:iterator value="books" status="index">

                                 <!-- 判斷序號是否爲奇數 -->

                                 <s:if test="#index.odd == true">

                                     <tr style="background-color:#cccccc">

                                  </s:if>

                                 <!-- 判斷迭代元素的序號是否不爲偶數 -->

                                  <s:else>

                                  <tr>

     </s:else>

                                    <td>書名:</td>

                                    <td><s:property/></td>

                                 </tr>

                                 </s:iterator>

                                 </table>

    </body>

</html>

上面的JSP頁面使用了Struts 2的標籤庫,所以必須在JSP頁面的首部添加taglib指令,該taglib指令用於導入標籤庫。

 提示  若是須要使用某個標籤庫中的標籤,則必須在頁面的開始導入該標籤庫。

頁面中使用Struts 2的iterator標籤迭代輸出ValueStack中的books數組,併爲每一個數組元素定義了一個序號:index。經過判斷序號是否爲奇數,若是行序號爲奇數,則輸出一個有背景色的表格行;不然輸出一個無背景色的表格行。

在瀏覽器中再次向getBooks.action發送請求(發送請求以前,必須先登陸本系統),將看到如圖2.6所示的界面。

 

圖2.6  使用Struts 2標籤改善後的輸出界面

上面頁面的輸出效果與圖2.5並無太大的不一樣,只是使用不一樣顏色來分隔了記錄行。這也得益於Struts 2標籤庫的簡潔。

關鍵在於2.5.4節中的JSP頁面代碼與本節頁面代碼的差別:前面JSP頁面使用了大量的Java腳本,讓整個頁面的代碼看起來很是凌亂,下降了可閱讀性、可維護性。但本頁面中僅使用Struts 2腳本控制輸出,徹底消除了頁面中的Java腳本,下降了該頁面的後期維護成本。

2.6.2  使用UI標籤簡化表單頁面

前面已經提到過,Struts 2的一個重要組件就是標籤庫。Struts 2標籤庫中不只提供了前面所示的基本控制、數據輸出等功能,還提供了很是豐富的UI組件,除了提供系列的主題相關標籤外,還提供了一系列的表單相關的標籤。

Struts 2爲經常使用表單域都提供了對應的標籤,下面是經常使用的表單域標籤。

—  form:對應一個表單元素。

—  checkbox:對應一個複選框元素。

—  password:對應一個密碼輸入框。

—  radio:對應一個單選框元素。

—  reset:對應一個重設按鈕。

—  select:對應一個下拉列表框。

—  submit:對應一個提交按鈕。

—  textarea:對應一個多行文本域。

—  textfield:對應一個單行文本框。

關於這些界面相關的標籤,一樣將在第10章詳細介紹。下面將使用Struts 2的表單相關標籤簡化用戶登陸的login.jsp頁面,修改的login.jsp頁面的代碼以下:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<%@taglib prefix="s" uri="/struts-tags"%>

<html>

<head>

<title>登陸頁面</title>

</head>

<body>

<!-- 使用form標籤生成表單元素 -->

<s:form action="Login">

                                 <!-- 生成一個用戶名文本輸入框 -->

                                 <s:textfield name="username" label="用戶名"/>

                                 <!-- 生成一個密碼文本輸入框 -->

                                 <s:textfield name="password" label="密  碼"/>

                                 <!-- 生成一個提交按鈕 -->

                                 <s:submit value="登陸"/>

</s:form>

</body>

</html>

將該頁面與前面的表單頁面進行對比,咱們發現該頁面的代碼簡潔多了。由於使用了Struts 2的表單標籤,定義表單頁面也更加迅速。在瀏覽器中瀏覽該頁面,看到如圖2.7所示的界面。

 

圖2.7  使用Struts 2表單標籤後的表單頁

固然,Struts 2的標籤還有許多功能,此處先不詳述,本書的第10章將會詳細介紹Struts 2標籤的用法。

2.7  完成程序國際化

由於一個企業應用常常須要面對多區域的用戶,所以,程序國際化是一個企業應用必須實現的功能。Struts 2提供了很好的程序國際化支持。

2.7.1  定義國際化資源文件

Struts 2的程序國際化支持創建在Java程序國際化的基礎之上,關於Java程序的國際化筆者將在第9章簡要介紹。此處不會詳細介紹,但咱們要明白一個概念:程序國際化的設計思想是很是簡單的,其主要思想是:程序界面中須要輸出國際化信息的地方,咱們不要在頁面中直接輸出信息,而是輸出一個key值,該key值在不一樣語言環境下對應不一樣的字符串。當程序須要顯示時,程序將根據不一樣的語言環境,加載該key對應該語言環境下的字符串——這樣就能夠完成程序的國際化。

圖2.8顯示了程序國際化的示意圖。

 

圖2.8  程序國際化示意圖

從圖2.8能夠看出,若是須要程序支持更多的語言環境,只須要增長更多語言資源文件便可。

爲了給本應用增長程序國際化支持(支持英文和中文),則應該提供兩份語言資源文件。下面是本應用所使用的中文語言環境下資源文件的代碼。

loginPage=登陸頁面

errorPage=錯誤頁面

succPage=成功頁面

failTip=對不起,您不能登陸!

succTip=歡迎,${0},您已經登陸!

viewLink=查看做者李剛已出版的圖書

bookPageTitle=做者李剛已出版的圖書

bookName=書名:

user=用戶名

pass=密  碼

login=登陸

由於該資源文件中包含了非西歐字符,所以必須使用native2ascii命令來處理該文件。將上面文件保存在WEB-INF/classes路徑下,文件名爲「messageResouce.properties」。保存該文件後,必須使用native2ascii命令來處理該文件,處理該文件的命令格式爲:

native2ascii messageResouce.properties messageResouce_zh_CN.properties

上面命令將包含非西歐字符的資源文件處理成標準的ASCII格式,處理完成後生成了一份新文件:messageResouce _zh_CN.properties文件。這個文件的文件名符合資源文件的命名格式,資源文件的文件名命名格式爲:

basename_語言代碼_國家代碼.properties

當請求來自簡體中文的語言環境時,系統將自動使用這種資源文件中的內容輸出。

 注意  對於包含非西歐字符的資源文件,必定要使用native2assii命令來處理該文件,不然將看到一堆亂碼。

除此以外,還應該提供以下英文語言環境的資源文件。

loginPage=Login Page

errorPage=Error Page

succPage=Welcome Page

failTip=Sorry,You can't log in!

succTip=welcome,{0},you has logged in!

viewLink=View LiGang\'s Books

bookPageTitle=LiGang\'s Books

bookName=BookName:

user=User Name

pass=UserPass

login=Login

將上面資源文件保存在WEB-INF/classes路徑下,文件名爲「messageResouce_en_ US.properties」。當請求來自美國時,系統自動使用這份資源文件的內容輸出。

2.7.2  加載資源文件

Struts 2支持在JSP頁面中臨時加載資源文件,也支持經過全局屬性來加載資源文件。經過全局屬性加載資源文件更簡單,本應用使用全局屬性加載Struts 2國際化資源文件。

加載資源文件能夠經過struts.properties文件來定義,本應用的struts.properties文件僅有以下一行代碼:

//定義Struts 2的資源文件的baseName是messageResource

struts.custom.i18n.resources=messageResource

在struts.properties文件中增長上面的代碼定義後,代表該應用使用的資源文件的baseName爲「messageResouce」——這與咱們前面保存資源文件的baseName是一致的。

Struts 2默認加載WEB-INF/classes下的資源文件,在上一節中,咱們就是將資源文件保存在該路徑下的。若是將該資源文件保存在WEB-INF/classes的子目錄下,例如保存在WEB-INF/classes/lee路徑下,則須要修改struts.properties中的定義以下:

//定義Struts 2的資源文件的baseName是messageResource,且文件放在WEB-INF/

classes/lee路徑下

struts.custom.i18n.resources=lee.messageResource

2.7.3  輸出國際化信息

爲了讓程序能夠顯示國際化信息,則須要在JSP頁面中輸出key,而不是直接輸出字符串常量。

Struts 2提供了以下兩種方式來輸出國際化信息:

—  <s:text name="messageKey"/>:使用s:text標籤來輸出國際化信息。

—  <s:property value="%{getText("messageKey")}"/>:使用表達式方式輸出國際化信息。

所以,咱們再次修改表現層的JSP頁面,使用國際化標籤輸出國際化信息。修改後的showBook.jsp頁面代碼以下:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<%@taglib prefix="s" uri="/struts-tags"%>

<html>

                                 <head>

                                    <!-- 使用s:text輸出國際化信息 -->

        <title><s:text name="bookPageTitle"/></title>

    </head>

    <body>

                                 <table border="1" width="360">

                                 <!-- 使用s:text輸出國際化信息 -->

                                 <caption><s:text name="bookPageTitle"/></caption>

                                 <s:iterator value="books" status="index">

                                    <s:if test="#index.odd == true">

         <tr style="background-color:#cccccc">

       </s:if>

       <s:else>

         <tr>

       </s:else>

                                    <td><s:text name="bookName"/></td>

                                    <td><s:property/></td>

                                 </tr>

                                 </s:iterator>

                                 </table>

    </body>

</html>

咱們發現,上面的JSP頁面再也不包含任何直接字符串,而是所有經過<s:text name="..."/>來輸出國際化提示。

再次在瀏覽器瀏覽該頁面,將看到與圖2.7相同的界面。

從新設置瀏覽者所在的語言/區域選項,設置語言/區域選項請先進入「控制面板」,在控制面板中單擊「區域和語言選項」,進入如圖2.9所示的對話框。

若是咱們選擇「英語(美國)」選項,而後單擊「肯定」按鈕,將設置本地的語言環境爲美國英語。

再次向服務器請求login.jsp頁面,將看到如圖2.10所示的頁面。

            

圖2.9  設置語言/區域選項                  圖2.10  程序國際化的效果

若是咱們使用FireFox瀏覽器來瀏覽該頁面時,發現依然顯示中文界面——這是由於FireFox的語言環境並不受Windows系統的控制。爲了讓FireFox也使用美國英語環境,單擊FireFox瀏覽器菜單欄中的「工具」菜單,選擇「選項」菜單項,將出現「選項」對話框,單擊「高級」按鈕,將看到如圖2.11所示的界面。

在如圖2.11所示的對話框中單擊「選擇」按鈕,將出現「語言和字符編碼」對話框,在該對話框下面的下拉列表框中選擇「英語/美國 [en-us]」選項,如圖2.12所示,而後單擊下拉框右邊的「添加」按鈕,將添加了「英語/美國」的語言環境。並在該對話框上面的列表框中選擇「英語/美國 [en-us]」,而後單擊「上移」按鈕,將該選項移至最上面,讓整個頁面優先使用英語/美國的環境。

      

圖2.11  設置FireFox的語言環境         圖2.12  設置FireFox的語言環境爲英語/美國

再次使用FireFox瀏覽器瀏覽login.jsp頁面,將看到該頁面變成了英文界面。

2.8  增長數據校驗

在上面應用中,即便瀏覽者輸入任何用戶名、密碼,系統也會處理用戶請求。在咱們整個HelloWorld應用中,這種空用戶名、空密碼的狀況不會引發太大的問題。但若是數據須要保存到數據庫,或者須要根據用戶輸入的用戶名、密碼查詢數據,這些空輸入可能引發異常。

爲了不用戶的輸入引發底層異常,一般咱們會在進行業務邏輯操做以前,先執行基本的數據校驗。

2.8.1  繼承ActionSupport

ActionSupport類是一個工具類,它已經實現了Action接口。除此以外,它還實現了Validateable接口,提供了數據校驗功能。經過繼承該ActionSupport類,能夠簡化Struts 2的Action開發。

在Validatable接口中定義了一個validate()方法,重寫該方法,若是校驗表單輸入域出現錯誤,則將錯誤添加到ActionSupport類的fieldErrors域中,而後經過OGNL表達式負責輸出。

爲了讓Struts 2增長輸入數據校驗的功能,改寫程序中的LoginAction,增長重寫validate方法。修改後的LoginAction類代碼以下:

//Struts 2的Action類就是一個普通的Java類

public class LoginAction

{

                                 //下面是Action內用於封裝用戶請求參數的兩個屬性

                                  private String username;

                                 private String password;

                                 //username屬性對應的getter方法

                                  public String getUsername()

                                 {

                                  return username;

                                  }

                                 //username屬性對應的setter方法

                                  public void setUsername(String username)

                                 {

                                  this.username = username;

                                 }

                                 //password屬性對應的getter方法

                                  public String getPassword()

                                 {

                                  return password;

                                  }

                                 //password屬性對應的setter方法

                                  public void setPassword(String password)

                                 {

                                  this.password = password;

                                  }

                                 //處理用戶請求的execute方法

                                 public String execute() throws Exception

                                 {

                                    //當用戶請求參數的username等於scott,密碼請求參數爲tiger時,返回success

                                      字符串

                                    //不然返回error的字符串

                                  if (getUsername().equals("scott")

                                        && getPassword().equals("tiger") )

                                    {

                                   return "success";

                                  }

                                    else

                                    {

                                   return "error";

                                  }

                                  }

                                 //完成輸入校驗須要重寫的validate方法

                                 public void validate()

                                 {

                                    //若是用戶名爲空,或者用戶名爲空字符串

                                    if (getUsername() == null || getUsername().trim().equals(""))

                                    {

                                        //添加表單校驗錯誤

                                        addFieldError("username", "user.required");

                                    }

                                    //當密碼爲空,或者密碼爲空字符串時,添加表單校驗錯誤

                                    if (getPassword() == null || getPassword().trim().equals(""))

                                    {

                                        addFieldError("password", "pass.required");

                                    }

                                 }

}

上面的Action類重寫了validate方法,該方法會在執行系統的execute方法以前執行,若是執行該方法以後,Action類的fieldErrors中已經包含了數據校驗錯誤,請求將被轉發到input邏輯視圖處。

爲了在校驗失敗後,系統能將視圖轉入input處,必須在配置該Action時配置input屬性。下面是修改後login Action的配置片斷:

<!-- 定義login的Action -->

<action name="Login" class="lee.LoginAction">

                                 <!-- 定義input的邏輯視圖名,對應login.jsp頁面 -->

                                 <result name="input">/login.jsp</result>

                                 <!-- 定義error的邏輯視圖名,對應error.jsp頁面 -->

                                  <result name=" success ">/error.jsp</result>

                                 <!-- 定義welcome的邏輯視圖名,對應welcome.jsp頁面 -->

                                  <result name="success">/welcome.jsp</result>       

</action>

對比上面的Action配置與前面的Action配置,咱們發現該Action配置片斷中增長了input邏輯視圖的配置,該邏輯視圖映射到login.jsp頁面。

前面已經提到:當用戶提交請求時,請求獲得execute方法處理以前,先會被validate方法處理,若是該方法處理結束後,Action的fieldErrors裏的校驗錯誤不爲空,請求將被轉發給input邏輯視圖。若是咱們不輸入用戶名、密碼而直接提交表單,將看到如圖2.13所示的界面。

 

圖2.13  輸入校驗的界面

看到這裏也許讀者以爲很是神奇:咱們僅僅在Action添加了數據校驗錯誤,並未在輸入頁面輸出這些校驗錯誤信息,但圖2.13所示的頁面,卻能夠看到頁面已經輸出了這些校驗信息——這是由於Struts 2的標籤,上面的JSP頁面中表單使用的並非HTML表單,而是用了<s:form .../>標籤,Struts 2的<s:form ... />標籤已經具有了輸出校驗錯誤的能力。

 提示  Struts 2的<s:form .../>默認已經提供了輸出校驗錯誤的能力。

但上面的程序還存在一個問題:校驗信息的國際化。查看上面的Action類代碼發現:重寫validate方法時,若是發生校驗失敗的問題,校驗錯誤的提示信息是以硬編碼方式寫死了——這就失去了國際化的能力。

實際上,ActionSupport類已經提供了國際化信息的能力,它提供了一個getText(String key)方法,該方法用於從資源文件中獲取國際化信息。爲了讓校驗信息支持國際化,再次改寫Action裏的validate方法,改寫後的validate方法代碼以下:

//執行數據校驗的validate方法

public void validate()

{

                                 //若是用戶名爲空,或者爲空字符串

                                 if (getUsername() == null || getUsername().trim().equals(""))

                                 {

                                    //添加校驗錯誤提示,使用getText方法來使提示信息國際化

                                    addFieldError("username", getText("user.required"));

                                 }

                                 if (getPassword() == null || getPassword().trim().equals(""))

                                 {

                                    addFieldError("password", getText("pass.required"));

                                 }

}

在上面的validate方法中,添加校驗錯誤提示時,並非直接給出了錯誤提示的字符串,而是調用了getText方法來獲取錯誤提示。由於在Action中,使用getText方法來獲取了兩個國際化提示:user.required和pass.required,所以應該在國際化資源文件中添加這兩條提示信息。

 提示  ActionSupport增長了讓提示信息國際化的能力,ActionSupport提供的getText方法能夠根據資源文件加載得到國際化提示信息。

此時,若是沒有任何輸出,直接提交登陸表單,將看到如圖2.14所示的界面。

 

圖2.14  國際化數據校驗的錯誤提示

2.8.2  使用Struts 2的校驗框架

上面的輸入校驗是經過重寫ActionSupport類的validate方法實現的,這種方法雖然不錯,但須要大量重寫的validate方法——畢竟,重複書寫相同的代碼不是一件吸引人的事情。

相似於Struts 1,Struts 2也容許經過定義配置文件來完成數據校驗。Struts 2的校驗框架其實是基於XWork的validator框架。

下面仍是使用原來的Action類(即不重寫validate方法),卻增長一個校驗配置文件,校驗配置文件經過使用Struts 2已有的校驗器,完成對錶單域的校驗。Struts 2提供了大量的數據校驗器,包括表單域校驗器和非表單域校驗器兩種。

本應用主要使用了requiredstring校驗器,該校驗器是一個必填校驗器——指定某個表單域必須輸入。

下面是校驗規則的定義文件:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定校驗規則文件的DTD信息 -->

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator

                                 1.0.2//EN"

                                 "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<!-- 校驗規則定義文件的根元素 -->

<validators>

                                 <!-- 校驗第一個表單域:username -->

                                 <field name="username">

                                    <!-- 該表單域必須填寫 -->

                                  <field-validator type="requiredstring">

                                        <!-- 若是校驗失敗,顯示user.required對應的信息 -->

                                   <message key="user.required"/>

                                  </field-validator>

                                  </field>

                                 <!-- 校驗第二個表單域:password -->

                                  <field name="password">

                                  <field-validator type="requiredstring">

                                        <!-- 若是校驗失敗,顯示pass.required對應的信息 -->

                                   <message key="pass.required"/>

                                  </field-validator>

                                  </field>

</validators>

定義完該校驗規則文件後,該文件的命名應該遵照以下規則:

ActionName-validation.xml:其中ActionName就是須要校驗的Action的類名。

所以上面的校驗規則文件應該命名爲「LoginAction-validation.xml」,且該文件應該與Action類的class文件位於同一個路徑下。所以,將上面的校驗規則文件放在WEB-INF/classes/lee路徑下便可。

固然,在struts.xml文件的Action定義中,同樣須要定義input的邏輯視圖名,將input邏輯視圖映射到login.jsp頁面。

若是不輸入用戶名、密碼而提交表單,將再次看到如圖2.14所示的界面。在這種校驗方式下,無需書寫校驗代碼,只須要經過配置文件指定校驗規則便可,所以提供了更好的可維護性。

2.9  本章小結

本章以一個HelloWorld應用爲例,簡要介紹了Struts 2 MVC框架的基本流程,從Action類基本流程控制講起,詳細介紹瞭如何開發一個Struts 2應用。本章的後面部分在基本Struts 2應用基礎上,介紹了一些Struts 2的深刻應用,包括在Action中訪問HttpSession狀態,將Action處理結果傳回JSP頁面顯示,本應用也綜合應用了Struts 2的標籤庫、數據校驗、程序國際化等經常使用功能。經過閱讀本章的內容,讀者應該對Struts 2框架有一個大體的掌握。

相關文章
相關標籤/搜索