程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Struts 2.0中的轉換器(Converter)

Struts 2.0中的轉換器(Converter)

編輯:關於JAVA

在我已往的Struts 1.x項目經驗中,有個問題不時的出現——在創建FormBean時,對於某個屬性到底應該用String還是其它類型?

開發Web應用程序與開發傳統桌面應用程序不同,Web應用程序實際上是分布個不同的主機(當然也可以同一個主機,不過比較少見)上的兩個進程之間互交。這種互交建立在HTTP之上,它們互相傳遞是都是字符串。換句話說, 服務器可以的接收到的來自用戶的數據只能是字符串或字符數組,而在服務器上的對象中,這些數據往往有多種不同的類型,如日期(Date),整數(int),浮點數(float)或自定義類型(UDT)等,如圖1所示。因此,我們需要服務器端將字符串轉換為適合的類型。

圖1 UI與服務器對象關系

同樣的問題也發生在使用UI展示服務器數據的情況。HTML的Form控件不同於桌面應用程序可以表示對象,其值只能為字符串類型,所以我們需要通過某種方式將特定對象轉換成字符串。

要實現上述轉換,Struts 2.0中有位魔術師可以幫到你——Converter。有了它,你不用一遍又一遍的重復編寫諸如此類代碼:

Date birthday = DateFormat.getInstance(DateFormat.SHORT).parse(strDate);

<input type="text" value="<%= DateFormat.getInstance(DateFormat.SHORT).format(birthday) %>" />

好了,現在讓我們來看一個例子。

轉換器——Hello World

在我的上一篇文章《在Struts 2.0中國際化(i18n)您的應用程序》的最後我舉了一個可以讓用戶方便地切換語言的例子,下面例子與其相似,但實現方法不同。

首先,如《在Struts 2.0中國際化(i18n)您的應用程序》的第一個例子一樣,創建和配置默認的資源文件;

接著,新建源代碼文件夾下的tutorial包創建HelloWorld.java文件,代碼如下:

package tutorial;
import java.util.Locale;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
public class HelloWorld extends ActionSupport {
   private String msg;
   private Locale loc = Locale.US;
   public String getMsg() {
     return msg;
   }
   public Locale getLoc() {
     return loc;
   }
   public void setLoc(Locale loc) {
     this .loc = loc;
   }
   @Override
   public String execute() {
     // LocalizedTextUtil是Struts 2.0中國際化的工具類,<s:text>標志就是通過調用它實現國際化的
     msg = LocalizedTextUtil.findDefaultText( " HelloWorld " , loc);
     return SUCCESS;
   }
}

然後,在源代碼文件夾下的struts.xml加入如下代碼新建Action:

< package name ="ConverterDemo" extends ="struts-default" >
   < action name ="HelloWorld" class ="tutorial.HelloWorld" >
     < result > /HelloWorld.jsp </ result >
   </ action >
</ package >

再在Web文件夾下,新建 HelloWorld.jsp,代碼如下:

< %@ page  contentType ="text/html; charset=UTF-8" % >
< %@taglib prefix ="s" uri ="/struts-tags" % >
< html >
< head >
   < title > Hello World </ title >
</ head >
< body >
   < s:form action ="HelloWorld" theme ="simple" >
     Locale: < s:textfield name ="loc" /> &nbsp; < s:submit />
   </ s:form >
   < h2 >< s:property value ="msg" /></ h2 >
</ body >
</ html >

接下來,在源代碼文件夾的tutorial包中新建LocaleConverter.java文件,代碼如下:

package tutorial;
import java.util.Locale;
import java.util.Map;
public class LocaleConverter extends ognl.DefaultTypeConverter {
   @Override
   public Object convertValue(Map context, Object value, Class toType) {
     if (toType == Locale. class ) {
       String locale = ((String[]) value)[ 0 ];
       return new Locale(locale.substring( 0 , 2 ), locale.substring( 3 ));
     } else if (toType == String. class ) {
       Locale locale = (Locale) value;
       return locale.toString();
     }
     return null ;
   }
}

再接下來,在源代碼文件夾下新建xwork-conversion.properties,並在其中添加如下代碼:

java.util.Locale = tutorial.LocaleConverter

發布運行應用程序,在浏覽器中鍵入http://localhost:8080/Struts2_Converter/HelloWorld.action,輸出頁面如圖2所示:

圖2 HelloWorld英文輸出

在Locale輸入框中輸入“zh_CN”,按“Submit”提交,出現如圖3所示頁面:

圖3 HelloWorld中文輸出

上述例子中,Locale文本輸入框對應是Action中的類型為java.util.Locale的屬性loc,所以需要創建一個自定義轉變器實現兩者間的轉換。所有的Struts 2.0中的轉換器都必須實現ognl.TypeConverter接口。 為了簡單起見,OGNL包也為你提供了ognl.DefaultTypeConverter類去幫助您實現轉換器。在例子中,LocaleConverter繼承了ognl.DefaultTypeConverter,重載了其方法原型為“public Object convertValue(Map context, Object value, Class toType)”的方法。下面簡單地介紹一下函數的參數:

context——用於獲取當前的ActionContext

value——需要轉換的值

toType——需要轉換成的目標類型

實現轉換器,我們需要通過配置告訴Struts 2.0。我們可以通過以下兩種方法做到這點:

配置全局的類型轉換器,也即是上例的做法——在源代碼文件夾下,新建一個名為“xwork-conversion.properties”的配置文件,並在文件中加入“待轉換的類型的全名(包括包路徑和類名)=轉換器類的全名”對;

應用於某個特定類的類型轉換器,做法為在該類的包中添加一個格式為“類名-conversion.properties”的配置文件,並在文件中加入“待轉換的屬性的名字=轉換器類的全名”對。上面的例子也可以這樣配置——在源代碼文件夾的tutorial包下新建名為“HelloWorld-conversion.properties”文件,並在其中加入“loc=tutorial.LocaleConverter”。

在繼承DefaultTypeConverter時,如果是要將value轉換成其它非字符串類型時,要記住value是String[]類型,而不是String類型。它是通過request.getParameterValues(String arg)來獲得的,所以不要試圖將其強行轉換為String類型。 

已有的轉換器

對於一此經常用到的轉換器,如日期、整數或浮點數等類型,Struts 2.0已經為您實現了。下面列出已經實現的轉換器。

預定義類型,例如int、boolean、double等;

日期類型, 使用當前區域(Locale)的短格式轉換,即DateFormat.getInstance(DateFormat.SHORT);

集合(Collection)類型, 將request.getParameterValues(String arg)返回的字符串數據與java.util.Collection轉換;

集合(Set)類型, 與List的轉換相似,去掉相同的值;

數組(Array)類型, 將字符串數組的每一個元素轉換成特定的類型,並組成一個數組。

對於已有的轉換器,大家不必再去重新發明輪子。Struts在遇到這些類型時,會自動去調用相應的轉換器。 批量封裝對象(Bean)

不知道大家是否遇過這種情況,在一個頁面裡同時提交幾個對象。例如,在發布產品的頁面,同時發布幾個產品。我在之前一個項目就遇到過這種需求,當時用的是Struts 1.x。那是一個痛苦的經歷,我在Google搜了很久都沒有理想的結果。幸運的是,在Struts 2.0中這種痛苦將一去不復返。下面我就演示一下如何實現這個需求。

首先,在源代碼文件夾下的tutorial包中新建Product.java文件,內容如下:

package tutorial;
import java.util.Date;
publicclass Product {
   private String name;
   privatedouble price;
   private Date dateOfProduction;
   public Date getDateOfProduction() {
     return dateOfProduction;
   }
   publicvoid setDateOfProduction(Date dateOfProduction) {
     this.dateOfProduction = dateOfProduction;
   }
   public String getName() {
     return name;
   }
   publicvoid setName(String name) {
     this.name = name;
   }
   publicdouble getPrice() {
     return price;
   }
   publicvoid setPrice(double price) {
     this.price = price;
   }
}

然後,在同上的包下添加ProductConfirm.java類,代碼如下:

package tutorial;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
publicclass ProductConfirm extends ActionSupport {
   public List<Product> products;
   public List<Product> getProducts() {
     return products;
   }
   publicvoid setProducts(List<Product> products) {
     this.products = products;
   }
   @Override
   public String execute() {
     for(Product p : products) {
       System.out.println(p.getName() + " | "+ p.getPrice() +" | " + p.getDateOfProduction());
     }
     return SUCCESS;
   }
}

接看,在同上的包中加入ProductConfirm-conversion.properties,代碼如下:

Element_products=tutorial.Product

再在struts.xml文件中配置ProductConfirm Action,代碼片段如下:

<action name="ProductConfirm" class="tutorial.ProductConfirm">
   <result>/ShowProducts.jsp</result>
</action>

在WEB文件夾下新建AddProducts.jsp,內容如下:

<%@ page contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
   <title>Hello World</title>
</head>
<body>
   <s:form action="ProductConfirm" theme="simple">
     <table>
       <tr style="background-color:powderblue; font-weight:bold;">
         <td>Product Name</td>
         <td>Price</td>
         <td>Date of production</td>
       </tr>
       <s:iterator value="new int[3]" status="stat">
         <tr>
           <td><s:textfield name="%{'products['+#stat.index+'].name'}"/></td>
           <td><s:textfield name="%{'products['+#stat.index+'].price'}"/></td>
           <td><s:textfield name="%{'products['+#stat.index+'].dateOfProduction'}"/></td>
         </tr>
       </s:iterator>
       <tr>
         <td colspan="3"><s:submit /></td>
       </tr>
     </table>
   </s:form>
</body>
</html>

在同樣的文件夾下創建ShowProducts.jsp,內容如下:

<%@ page contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
   <title>Hello World</title>
</head>
<body>
   <table>
     <tr style="background-color:powderblue; font-weight:bold;">
       <td>Product Name</td>
       <td>Price</td>
       <td>Date of production</td>
     </tr>
     <s:iterator value="products" status="stat">
       <tr>
         <td><s:property value="name"/></td>
         <td>$<s:property value="price"/></td>
         <td><s:property value="dateOfProduction"/></td>
       </tr>
     </s:iterator>
   </table>
</body>
</html>

發布運行應用程序,在浏覽器中鍵入http://localhost:8080/Struts2_Converter/AddProducts.jsp,出現如圖4所示頁面:

圖4 添加產品頁面

按圖4所示,填寫表單,按“Submit”提交,出現圖5所示頁面:

圖5 查看產品頁面

查看服務器的控制台,有如下輸出:

Expert One-on-One J2EE Development without EJB | 39.99 | Mon Jun 2100:00:00 CST 2004
Pro Spring | 32.99 | Mon Jan 3100:00:00 CST 2005
Core J2EE Patterns: Best Practices and Design Strategies, Second Edition | 34.64 | Sat May 1000:00:00 CST 2003

上面的代碼並不復雜,但有幾點需要說明:

ProductConfirm文件中的for(Product p : productes)的寫法是J2SE 5.0中的新特性,作用遍歷products列表;

List<Product>也是J2SE 5.0的才有的泛型(Generic);

ProductConfirm-conversion.properties中“Element_products=tutorial.Product”是告訴Struts 2.0列表products的元素的類型為Product,而不是定義轉換器;

在AddProducts.jsp的<s:textfield>的name為“%{'products['+#stat.index+'].name'}”,%{exp}格式表示使用OGNL表達式,上述表達式的相當於<%= "products[" + stat.index + "].name" %>,至於<s:iterator>標志的用法可以參考我之前的文章《常用的Struts 2.0的標志(Tag)介紹》。

轉換錯誤處理

不知道大家在運行上面的例子時,有沒有填錯日期或數字情況,又或者您有沒有思考過這種情況?如果還沒有嘗試的朋友可以試一下,在第一行的Price和Date of production中輸入英文字母,然後按“Submit”提交。你會看到頁面為空白,再看一下服務器的控制台輸出,有如下語句: 警告: No result defined for action tutorial.ProductConfirm and result input,它提示我們沒有為Action定義輸入結果,所以,我們應該在源代碼文件夾下的struts.xml中的ProductConfirm Action中加入以下代碼:

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

重新加載應用程序,刷新浏覽器重新提交請求,這時頁面返回AddProducts.jsp,格式錯誤的輸入框的值被保留,如下圖6所示:

圖6 沒有提示的錯返回頁面

當然,我們還可以在頁面上加上錯誤提示信息,通過在AddProducts.jsp的“<body>”後,加入下面代碼可以實現:

<div style="color:red">
   <s:fielderror />
</div>

刷新浏覽器,重新提交請求,出現如圖7所示頁面:

圖7 帶提示的錯返回頁面

以上的功能的都是通過Struts 2.0裡的一個名為conversionError的攔截器(interceptor)工作,它被注冊到默認攔截器棧(default interceptor stack)中。Struts 2.0在轉換出錯後,會將錯誤放到ActionContext中,在conversionError的作用是將這些錯誤封裝為對應的項錯誤(field error),因此我們可以通過<s:fielderror />來將其在頁面上顯示出來。另外,大家看第二和第三行的Price都被賦為0.0的值,而第一行則保留其錯誤值。這同樣是conversionError的功勞——沒有出錯的行調用的products[index].price(默認值為0.0),而出錯的行則會被賦為頁面所提交的錯誤值,這樣可以提供更好的用戶體驗。

總結

Struts 2.0的轉換器簡化的WEB應用程序的模型,為我們的編程帶來極大的方便。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved