在我已往的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)”的方法。下面简单地介绍一下函数的参数:

  1. context——用于获取当前的ActionContext
  2. value——需要转换的值
  3. toType——需要转换成的目标类型

实现转换器,我们需要通过配置告诉Struts 2.0。我们可以通过以下两种方法做到这点:

  1. 配置全局的类型转换器,也即是上例的做法——在源代码文件夹下,新建一个名为“xwork-conversion.properties”的配置文件,并在文件中加入“待转换的类型的全名(包括包路径和类名)=转换器类的全名”对;
  2. 应用于某个特定类的类型转换器,做法为在该类的包中添加一个格式为“类名-conversion.properties”的配置文件,并在文件中加入“待转换的属性的名字=转换器类的全名”对。上面的例子也可以这样配置——在源代码文件夹的tutorial包下新建名为“HelloWorld-conversion.properties”文件,并在其中加入“loc=tutorial.LocaleConverter”。
在继承DefaultTypeConverter时,如果是要将value转换成其它非字符串类型时,要记住value是String[]类型,而不是String类型。它是通过request.getParameterValues(String arg)来获得的,所以不要试图将其强行转换为String类型。

已有的转换器

对于一此经常用到的转换器,如日期、整数或浮点数等类型,Struts 2.0已经为您实现了。下面列出已经实现的转换器。

  1. 预定义类型,例如int、boolean、double等;
  2. 日期类型, 使用当前区域(Locale)的短格式转换,即DateFormat.getInstance(DateFormat.SHORT);
  3. 集合(Collection)类型, 将request.getParameterValues(String arg)返回的字符串数据与java.util.Collection转换;
  4. 集合(Set)类型, 与List的转换相似,去掉相同的值;
  5. 数组(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

上面的代码并不复杂,但有几点需要说明:

  1. ProductConfirm文件中的for(Product p : productes)的写法是J2SE 5.0中的新特性,作用遍历products列表;
  2. List<Product>也是J2SE 5.0的才有的泛型(Generic);
  3. ProductConfirm-conversion.properties中“Element_products=tutorial.Product”是告诉Struts 2.0列表products的元素的类型为Product,而不是定义转换器;
  4. 在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应用程序的模型,为我们的编程带来极大的方便。

转载于:https://www.cnblogs.com/fhj2050/archive/2008/04/02/1134459.html

转换器(Converter)——Struts 2.0中的魔术师相关推荐

  1. 转换器(Converter)—Struts 2.0中的魔术师

    本系列文章导航 为Struts 2.0做好准备 Struts 2的基石--拦截器(Interceptor) 常用的Struts 2.0的标志(Tag)介绍 在Struts 2.0中国际化(i18n)您 ...

  2. 转换器(Converter)Struts 2.0中的魔术师

    转换器(Converter)--Struts 2.0中的魔术师  在我已往的Struts 1.x项目经验中,有个问题不时的出现--在创建FormBean时,对于某个属性到底应该用String还是其它类 ...

  3. 在Struts 2.0中国际化(i18n)您的应用程序

    国际化是商业系统中不可或缺的一部分,所以无论您学习的是什么Web框架,它都是必须掌握的技能. 其实,Struts 1.x在此部分已经做得相当不错了.它极大地简化了我们程序员在做国际化时所需的工作,例如 ...

  4. 学习Struts 2.0系列文章

    文章来自:[url]http://www.blogjava.net/max/[/url],敬请关注作者的Blog 一. 为Struts 2_0做好准备 二. 常用的Struts 2.0的标记(Tag) ...

  5. 实例解析:OperaMasks2.0中的DataGrid之一:定义DataGrid

    OperaMasks2.0中的DataGrid之一:定义DataGrid 1. 新版DataGrid介绍 2. 简单的动态定义表格 2.1. 准备数据 2.2. 定义简单的动态表格 2.3. Grid ...

  6. Spring MVC自定义类型转换器Converter、参数解析器HandlerMethodArgumentResolver

    文章目录 一.前言 二.类型转换器Converter 1.自定义类型转换器 三.参数解析器 1.自定义分页参数解析器 2.自定义注解参数解析器 一.前言 Spring MVC源码分析相关文章已出: S ...

  7. ASP.NET Core 3.0中使用动态控制器路由

    原文:Dynamic controller routing in ASP.NET Core 3.0 作者:Filip W 译文:https://www.cnblogs.com/lwqlun/p/114 ...

  8. 在.Net Core 3.0中尝试新的System.Text.Json API

    .NET Core 3.0提供了一个名为System.Text.Json的全新命名空间,它支持reader/writer,文档对象模型(DOM)和序列化程序.在此博客文章中,我将介绍它如何工作以及如何 ...

  9. 常用的Struts 2.0的标志(Tag)介绍

    在上一篇文章<为Struts 2.0做好准备>中,我过于详细地介绍了Struts 2.0开发环境和运行环境的配置,所以,本文很少涉及的以上两方面的细节.如果,您看完<为Struts ...

  10. java通过struts实现web中的文件下载

    上一篇已经记录了 web中的文件上传功能 java通过struts实现web中的文件上传 本篇记录下载功能 定义一个Action类,FileDownload.java package com.stru ...

最新文章

  1. 小麦的一生矢量图收藏贴-从种子的萌发到完熟
  2. monty python喜剧-看美剧学托福:最受美国人喜欢的100部美剧
  3. paraView做动画(终极教程)
  4. 嵌入式软件开发工程师谈软件架构的设计
  5. RDD和DataFrame和Dataset比较
  6. python中的thread_深入理解Python中的ThreadLocal变量(上)
  7. Eclipse使用常见设置
  8. AUTOCAD——圆命令
  9. 计算机网络信息安全风险评估准则,计算机网络信息安全风险评估准则及方法研究.pdf...
  10. 服务器网卡游戏性能,关于服务器网卡的选择 有同学虚拟盘速度慢 进游戏速度慢的请看 - 系统+虚拟盘 - 顺网科......
  11. [JS]Tue Feb 01 2022 000000 GMT+0800 (中国标准时间) 日期格式化
  12. 高清美图和GIF动图素材网站推荐,拿走不谢!
  13. 阿里云镜像服务 vpc地址 无法 pull
  14. 滴滴跨端框架 Chameleon 正式支持快应用
  15. cocos 安卓打包相关
  16. 什么是大数据,大数据最缺什么样的人才?
  17. 手机里微信更换了头像电脑没同步
  18. Pytorch中torchvision.datasets.ImageFolder报错:Found 0 files in subfolders of:XXX,完美解决!
  19. Python 列表元组拆分为多个列表
  20. 2022Java面试心得,35岁的程序员被裁

热门文章

  1. 【学术研究】保持高效论文写作的10个原则!
  2. 百度盘搜失效?这款网盘搜索神器万万别错过!
  3. 【论文】哈工大SCIR Lab | EMNLP 2019 基于BERT的跨语言上下文相关词向量在零样本依存分析中的应用...
  4. 介绍4个大神常用而你不常用的python函数--assert,map,filter,reduce
  5. 每日一题[LeetCode 689]三个无重叠子数组的最大和
  6. 产品经理应该扮演的几种角色
  7. 4.6 Spark SQL 数据清洗
  8. sql语法、特殊符号及正则表达式的使用
  9. csv 逗号数量不一样_lexin_common系列之CSV
  10. Flutter进阶第4篇: 实现简单的新闻系统渲染新闻详情数据以及用flutter_html解析html