轉(zhuǎn)換器(Converter)

          Posted on 2008-12-12 13:26 李春生 閱讀(300) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): struts2.0
           

          五、轉(zhuǎn)換器(Converter——Struts 2.0中的魔術(shù)師

          在我已往的Struts 1.x項(xiàng)目經(jīng)驗(yàn)中,有個(gè)問(wèn)題不時(shí)的出現(xiàn)——在創(chuàng)建FormBean時(shí),對(duì)于某個(gè)屬性到底應(yīng)該用String還是其它類(lèi)型?

          開(kāi)發(fā)Web應(yīng)用程序與開(kāi)發(fā)傳統(tǒng)桌面應(yīng)用程序不同,Web應(yīng)用程序?qū)嶋H上是分布個(gè)不同的主機(jī)(當(dāng)然也可以同一個(gè)主機(jī),不過(guò)比較少見(jiàn))上的兩個(gè)進(jìn)程之間互交。這種互交建立在HTTP之上,它們互相傳遞是都是字符串。換句話說(shuō), 服務(wù)器可以的接收到的來(lái)自用戶的數(shù)據(jù)只能是字符串或字符數(shù)組,而在服務(wù)器上的對(duì)象中,這些數(shù)據(jù)往往有多種不同的類(lèi)型,如日期(Date),整數(shù)(int),浮點(diǎn)數(shù)(float)或自定義類(lèi)型(UDT)等,如圖1所示。因此,我們需要服務(wù)器端將字符串轉(zhuǎn)換為適合的類(lèi)型。

          圖1 UI與服務(wù)器對(duì)象關(guān)系
          1 UI與服務(wù)器對(duì)象關(guān)系

          同樣的問(wèn)題也發(fā)生在使用UI展示服務(wù)器數(shù)據(jù)的情況。HTMLForm控件不同于桌面應(yīng)用程序可以表示對(duì)象,其值只能為字符串類(lèi)型,所以我們需要通過(guò)某種方式將特定對(duì)象轉(zhuǎn)換成字符串。

          要實(shí)現(xiàn)上述轉(zhuǎn)換,Struts 2.0中有位魔術(shù)師可以幫到你——Converter。有了它,你不用一遍又一遍的重復(fù)編寫(xiě)諸如此類(lèi)代碼:

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

          好了,現(xiàn)在讓我們來(lái)看一個(gè)例子。

          轉(zhuǎn)換器——Hello World

          在我的上一篇文章《Struts 2.0中國(guó)際化(i18n)您的應(yīng)用程序》的最后我舉了一個(gè)可以讓用戶方便地切換語(yǔ)言的例子,下面例子與其相似,但實(shí)現(xiàn)方法不同。

          首先,如《Struts 2.0中國(guó)際化(i18n)您的應(yīng)用程序》的第一個(gè)例子一樣,創(chuàng)建和配置默認(rèn)的資源文件;

          接著,新建源代碼文件夾下的tutorial包創(chuàng)建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() {
                 
          // LocalizedTextUtilStruts 2.0中國(guó)際化的工具類(lèi),<s:text>標(biāo)志就是通過(guò)調(diào)用它實(shí)現(xiàn)國(guó)際化的
                 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 >

          接下來(lái),在源代碼文件夾的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 ;
             }

          }

          再接下來(lái),在源代碼文件夾下新建xwork-conversion.properties,并在其中添加如下代碼:

          java.util.Locale = tutorial.LocaleConverter

          發(fā)布運(yùn)行應(yīng)用程序,在瀏覽器中鍵入http://localhost:8080/Struts2_Converter/HelloWorld.action,輸出頁(yè)面如圖2所示:

          圖2 HelloWorld英文輸出   圖3 HelloWorld中文輸出

          2 HelloWorld英文輸出                                                  3 HelloWorld中文輸出

          Locale輸入框中輸入“zh_CN”,按“Submit”提交,出現(xiàn)如圖3所示頁(yè)面:

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

          1.                   context——用于獲取當(dāng)前的ActionContext

          2.                   value——需要轉(zhuǎn)換的值

          3.                   toType——需要轉(zhuǎn)換成的目標(biāo)類(lèi)型

          實(shí)現(xiàn)轉(zhuǎn)換器,我們需要通過(guò)配置告訴Struts 2.0。我們可以通過(guò)以下兩種方法做到這點(diǎn):

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

          2.                   應(yīng)用于某個(gè)特定類(lèi)的類(lèi)型轉(zhuǎn)換器,做法為在該類(lèi)的包中添加一個(gè)格式為類(lèi)名-conversion.properties”的配置文件,并在文件中加入待轉(zhuǎn)換的屬性的名字=轉(zhuǎn)換器類(lèi)的全名對(duì)。上面的例子也可以這樣配置——在源代碼文件夾的tutorial包下新建名為“HelloWorld-conversion.properties”文件,并在其中加入“loc=tutorial.LocaleConverter”

          在繼承DefaultTypeConverter時(shí),如果是要將value轉(zhuǎn)換成其它非字符串類(lèi)型時(shí),要記住valueString[]類(lèi)型,而不是String類(lèi)型。它是通過(guò)request.getParameterValues(String arg)來(lái)獲得的,所以不要試圖將其強(qiáng)行轉(zhuǎn)換為String類(lèi)型。

          已有的轉(zhuǎn)換器

          對(duì)于一此經(jīng)常用到的轉(zhuǎn)換器,如日期、整數(shù)或浮點(diǎn)數(shù)等類(lèi)型,Struts 2.0已經(jīng)為您實(shí)現(xiàn)了。下面列出已經(jīng)實(shí)現(xiàn)的轉(zhuǎn)換器。

          1.                   預(yù)定義類(lèi)型,例如intbooleandouble等;

          2.                   日期類(lèi)型, 使用當(dāng)前區(qū)域(Locale)的短格式轉(zhuǎn)換,即DateFormat.getInstance(DateFormat.SHORT)

          3.                   集合(Collection)類(lèi)型, request.getParameterValues(String arg)返回的字符串?dāng)?shù)據(jù)與java.util.Collection轉(zhuǎn)換;

          4.                   集合(Set)類(lèi)型, List的轉(zhuǎn)換相似,去掉相同的值;

          5.                   數(shù)組(Array)類(lèi)型, 將字符串?dāng)?shù)組的每一個(gè)元素轉(zhuǎn)換成特定的類(lèi)型,并組成一個(gè)數(shù)組。

          對(duì)于已有的轉(zhuǎn)換器,大家不必再去重新發(fā)明輪子。Struts在遇到這些類(lèi)型時(shí),會(huì)自動(dòng)去調(diào)用相應(yīng)的轉(zhuǎn)換器。

          批量封裝對(duì)象(Bean

          不知道大家是否遇過(guò)這種情況,在一個(gè)頁(yè)面里同時(shí)提交幾個(gè)對(duì)象。例如,在發(fā)布產(chǎn)品的頁(yè)面,同時(shí)發(fā)布幾個(gè)產(chǎn)品。我在之前一個(gè)項(xiàng)目就遇到過(guò)這種需求,當(dāng)時(shí)用的是Struts 1.x。那是一個(gè)痛苦的經(jīng)歷,我在Google搜了很久都沒(méi)有理想的結(jié)果。幸運(yùn)的是,在Struts 2.0中這種痛苦將一去不復(fù)返。下面我就演示一下如何實(shí)現(xiàn)這個(gè)需求。

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

          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類(lèi),代碼如下:

          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,內(nèi)容如下:

          <%@ 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>

          在同樣的文件夾下創(chuàng)建ShowProducts.jsp,內(nèi)容如下:

          <%@ 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>

          發(fā)布運(yùn)行應(yīng)用程序,在瀏覽器中鍵入http://localhost:8080/Struts2_Converter/AddProducts.jsp,出現(xiàn)如圖4所示頁(yè)面:

          圖4 添加產(chǎn)品頁(yè)面

          4 添加產(chǎn)品頁(yè)面

          按圖4所示,填寫(xiě)表單,按“Submit”提交,出現(xiàn)圖5所示頁(yè)面:
          圖5 查看產(chǎn)品頁(yè)面
          5 查看產(chǎn)品頁(yè)面

          查看服務(wù)器的控制臺(tái),有如下輸出:

          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

          上面的代碼并不復(fù)雜,但有幾點(diǎn)需要說(shuō)明:

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

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

          3. ProductConfirm-conversion.properties“Element_products=tutorial.Product”是告訴Struts 2.0列表products的元素的類(lèi)型為Product,而不是定義轉(zhuǎn)換器;

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

          轉(zhuǎn)換錯(cuò)誤處理

          不知道大家在運(yùn)行上面的例子時(shí),有沒(méi)有填錯(cuò)日期或數(shù)字情況,又或者您有沒(méi)有思考過(guò)這種情況?如果還沒(méi)有嘗試的朋友可以試一下,在第一行的PriceDate of production中輸入英文字母,然后按“Submit”提交。你會(huì)看到頁(yè)面為空白,再看一下服務(wù)器的控制臺(tái)輸出,有如下語(yǔ)句: 警告: No result defined for action tutorial.ProductConfirm and result input,它提示我們沒(méi)有為Action定義輸入結(jié)果,所以,我們應(yīng)該在源代碼文件夾下的struts.xml中的ProductConfirm Action中加入以下代碼:

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

          重新加載應(yīng)用程序,刷新瀏覽器重新提交請(qǐng)求,這時(shí)頁(yè)面返回AddProducts.jsp,格式錯(cuò)誤的輸入框的值被保留,如下圖6所示:
          圖6 沒(méi)有提示的錯(cuò)返回頁(yè)面
          6 沒(méi)有提示的錯(cuò)返回頁(yè)面

          當(dāng)然,我們還可以在頁(yè)面上加上錯(cuò)誤提示信息,通過(guò)在AddProducts.jsp“<body>”后,加入下面代碼可以實(shí)現(xiàn):

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

          刷新瀏覽器,重新提交請(qǐng)求,出現(xiàn)如圖7所示頁(yè)面:

          圖7 帶提示的錯(cuò)返回頁(yè)面

          7 帶提示的錯(cuò)返回頁(yè)面

          以上的功能的都是通過(guò)Struts 2.0里的一個(gè)名為conversionError的攔截器(interceptor)工作,它被注冊(cè)到默認(rèn)攔截器棧(default interceptor stack)中。Struts 2.0在轉(zhuǎn)換出錯(cuò)后,會(huì)將錯(cuò)誤放到ActionContext中,在conversionError的作用是將這些錯(cuò)誤封裝為對(duì)應(yīng)的項(xiàng)錯(cuò)誤(field error),因此我們可以通過(guò)<s:fielderror />來(lái)將其在頁(yè)面上顯示出來(lái)。另外,大家看第二和第三行的Price都被賦為0.0的值,而第一行則保留其錯(cuò)誤值。這同樣是conversionError的功勞——沒(méi)有出錯(cuò)的行調(diào)用的products[index].price(默認(rèn)值為0.0),而出錯(cuò)的行則會(huì)被賦為頁(yè)面所提交的錯(cuò)誤值,這樣可以提供更好的用戶體驗(yàn)。

          總結(jié)

          Struts 2.0的轉(zhuǎn)換器簡(jiǎn)化的WEB應(yīng)用程序的模型,為我們的編程帶來(lái)極大的方便。

           

          posts - 5, comments - 10, trackbacks - 0, articles - 23

          Copyright © 李春生

          主站蜘蛛池模板: 法库县| 柳州市| 通辽市| 太谷县| 永城市| 潞西市| 武乡县| 兴宁市| 开远市| 萝北县| 承德市| 麦盖提县| 基隆市| 永德县| 得荣县| 泗阳县| 商河县| 舒兰市| 昂仁县| 海伦市| 柞水县| 东明县| 双江| 肥西县| 密云县| 灵璧县| 措勤县| 寿阳县| 滕州市| 吴川市| 贺兰县| 军事| 临江市| 大田县| 开封市| 漳浦县| 曲沃县| 涿州市| 洛川县| 陆川县| 长兴县|