一、Struts的转换器
Struts对ActionForm的自动搜集过程如下:
将页面数据放到map中,其中map的key为页面中的名称,map中的value为页面中的value值
调用第三方BeanUtils的setProperties方法,将map中的值逐个设置到ActionForm实例上,对于ActionForm中的每个属性根据类型调用相应的Converter,然后调用相应的convert方法,将相应的字符串转换成ActionForm中指定的类型;
Struts转换器默认可以把一个字符串转换成int、float、byte等等一些基本类型而对于日期,则只能处理java.sql.Date中的yyyy-mm-dd格式的,这时候就需要自己写转换器,Struts的conversion转换器,所有的表单提交的服务器上的都是字符串
在传递表单数据的时候会遇到这种情况。在formbeen中或者实体类中定义的类型是Date,Integer等对象类型。那么类型为String类型的传递进去的时候无法完成数据转换。这就需要使用struts的转换器。
自定义转换器的实现步骤:
1、实现converter接口,实现convert方法
2、将实现converter注册,通常情况采用servlet注册,采用servlet注册需要注意标签的配置,必须指定load-on-startup属性值或者,采用Struts plugin的方式注册
下面以日期为例:
<label>时间:</label><input type="text" name="time">
package org.lxh.converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.commons.beanutils.Converter; /** * @author xudongwang 2011-4-28 * */ public class UtilDateConverter implements Converter { @Override public Object convert(Class type, Object value) { System.out.println("进入自定义的转换器"); Date date = null; if(value instanceof String) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); try { date = format.parse((String)value); } catch (ParseException e) { return null; } } return date; } }
定义好converter以后要在项目中注册。为了保证项目启动后最初就进行注册,可以考虑在ActionServlet中完成注册。所以我们可以写一个继承了ActionServlet类的Servelet类,并重构init方法进行注册。要注意的是,如果在web.xml文件中设置了初始参数convertNull为true。注册的代码要写在super.init()之后。因为ActionServlet的init方法会调用一个ConvertUtils.deregister()方法,把所有的转换器清除。
以上面的日期转换器为例,具体注册的代码如下:
package org.lxh.servlet; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import org.apache.commons.beanutils.ConvertUtils; import org.lxh.converter.UtilDateConverter; /** * @author xudongwang 2011-4-28 * */ public class UtilDateConverterServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("进入servlet"); //action在对form-bean进行赋值的时候当发现有java.util.Date.class类型的则进入UtilDateConverter的转换器 //使用servlet是因为web.xml中进行配置使得服务器启动时就可以使用了 ConvertUtils.register(new UtilDateConverter(), Date.class); } }
注册完的同时,不要忘记把自己定义的ActionServlet在web.xml文件中配置好。这样,表单中的日期文本就可以传入Date类型的属性中了。
web.xml:
<servlet> <servlet-name>utilDateConverterServlet</servlet-name> <servlet-class>org.lxh.servlet.UtilDateConverterServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
但是还有一个问题,当发生错误需要进行数据回显时,<html:text>标签会从formbeen中读取数据,这个时候读取出的是Date类型的toString方法显示出的值,不是原来自己填写的值了。
struts-config.xml:
<form-beans> <form-bean name="loginForm" type="org.lxh.form.LoginForm"></form-bean> </form-beans> <action-mappings> <!-- 这里的name是通过map的键值对来进行匹配,同时通过conversion进行struts下的类型转换 --> <!-- unknown="true"表示如果没有xx.do则会跳转到错误页面 --> <action path="/login" name="loginForm" type="org.lxh.action.LoginAction" unknown="true"> <forward name="error" path="/error.jsp"></forward> <forward name="success" path="/success.jsp"></forward> </action> </action-mappings>
LoginAction:
public class LoginAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { LoginForm loginForm = (LoginForm)form; System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(loginForm.getTime()));
要想进行双向转换,struts1已经没法做到了。struts2才可以做到。要在struts1达到我们预期的效果,可以使用其他的途径。
例如使用jstl的fmt标签。把formbeen中的Date类型的数据按一定格式转成String类型,然后再赋值给<html:text>。
示例代码如下:
<fmt:formatDate value="${userForm.user.birthday}" type="date" pattern="yyyy-MM-dd" var="birthday"/>
日期:<html:text property="user.birthday" value="${birthday}"></html:text> <br/>
上面有提到一个convertNull的参数。这个参数的作用要说明一下。
当我们在实体类或者formbeen中定义属性时,若是基本数据类型,那么struts标签在初始化时会先初始化formbeen,那么这个基本数据类型会先初始化为默认值。比如int类型就是0.那么在载入表单页面时,数据就会显示为0.这样非常不理想,明明没有填入任何数据,会显示出0.那么我们可以定义这个基本数据类型为包装类型,比如int就可以定义为Integer。它的默认值是null,null在struts的表单标签中是不显示的。但是当我们在这个标签中不填任何数据,当提交发生错误需要数据回显时问题就来了。struts会自动把null进行转换。Integer类型的null会转化成0.显示出来又会是0.解决这个问题的办法就是设置convertNull为true或者1或者yes或者on还有1都可以。这个可以从源代码中看到。它的作用就是忽略自动null转换
二、国际化
国际化是指应用程序运行时,可根据客户端请求来自的国家/地区、语言的不同而显示不同的界面,例如如果请求来自中文操作系统的客户端,则应用程序中的各种标签,错误提示和帮助等都使用中文
Struts的国际化基于java的国际化,将中文转换成ascii
native2ascii 源文件生成的文件
或者在jdk中用native2ascii生成
a、java中的国际化:
java程序的国际化思路是将应用程序中的标签、提示等信息放在资源文件中,每个程序需要所支持的国家/语言,都必须提供对应的资源文件,其资源文件是key-value对,每个资源文件中的key是不变的,但value则随着不同国家/语言而变化
国际化主要通过如下三个类完成:
java.util.resourceBundle:对应用于加载一个资源包
java.util.Local:对应一个特定的国家/地区语言环境
java.text.MessageFormat:用于将消息格式化
package org.lxh.demo; import java.util.Locale; import java.util.ResourceBundle; /** * @author xudongwang 2011-5-5 * */ public class Test { public static void main(String[] args) { // Locale locale = Locale.getDefault(); Locale locale = new Locale("en", "us"); ResourceBundle bundle = ResourceBundle.getBundle("myResource", locale); String name = (String)bundle.getObject("name"); System.out.print(name); } }
说明:
java不可能支持所有国家和语言,可以通过Locale[] localeList = Locale.getAvailableLocales()获得java所支持的国家和语言的集合
myResource_en.properties(name=hello) myResource_zh_CN.properties(name=\u5434\u4F73\u4FCA) myResource_zh.properties(name=sss)
当使用第一种默认的方式时,会使用myResource_zh_CN.properties资源文件;
注意上面资源包的命名规则
b、Struts的国际化:
1、myResource_zh_CN.properties:
name=\u7528\u6237\u540D\uFF1A error.name={0} please login again!
资源文件说明:
1、myResource是国际化信息文件名(资源文件的格式:资源文件名 + "_" + 语言码 + "_" + 国家码),放在src下. 提供不同版本的国际化资源文件,中文需要采用native2ascii转换成unicode
2、加载资源文件的顺序:
a、搜索所有国家和语言都匹配的资源文件
b、如果上面的没有,在搜索语言匹配的文件
c、最后搜索资源文件名匹配的文件
2、struts-config.xml:
<!-- 相当于ResourceBundle.getBundle("myResource", Locale.getDefault()); --> <message-resources parameter="myResource"></message-resources>
配置文件说明;
1、message-resources元素中的parameter指定了资源文件的位置;
2、Struts实现国际化,必须将ActionServlet配置成load-on-startup的servlet,只有这样才可以保证在启动应用时加载该资源文件;
3、jsp页面上:
<bean:message key="name" />
说明:
key属性对应了资源文件中的key,Struts会自动根据浏览器的语言设置,从资源文件中取得相应的value