【Java EE 学习 35 上】【strus2】【类型转换器】【struts2和Servlet API解耦】【国际化问题】【资源文件乱码问题已经解决】
一、类型转换器
1.在动作类action中,声明和表单中name属性的值同名的属性,提供get和set方法,struts2就可以通过反射机制,从页面中获取对应的内容
1 package com.kdyzm.convert; 2 3 4 import com.opensymphony.xwork2.ActionSupport; 5 6 public class TypeConvertAction extends ActionSupport{ 7 private static final long serialVersionUID = 5695452524134841455L; 8 private int id; 9 private String name; 10 public int getId() { 11 return id; 12 } 13 14 public void setId(int id) { 15 this.id = id; 16 } 17 18 public String getName() { 19 return name; 20 } 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 26 @Override 27 public String execute() throws Exception { 28 System.out.println("访问了默认的execute方法!"); 29 System.out.println("id="+id); 30 System.out.println("name="+name); 31 return "success"; 32 } 33 }
2.并非所有类型的值struts2框架都能转化,比如经典的Date类型的数值(标准格式的yyyy-MM-dd可以)。提示错误信息:
九月 08, 2015 9:56:09 上午 com.opensymphony.xwork2.interceptor.ParametersInterceptor error 严重: Developer Notification (set struts.devMode to false to disable this message): Unexpected Exception caught setting 'register_date' on 'class com.kdyzm.convert.TypeConvertAction: Error setting expression 'register_date' with value ['2015/2/1', ] 九月 08, 2015 9:56:09 上午 com.opensymphony.xwork2.util.LocalizedTextUtil warn 警告: Missing key [invalid.fieldvalue.register_date] in bundles [[org/apache/struts2/struts-messages, com/opensymphony/xwork2/xwork-messages]]!
该错误提示信息提示了处理该转换的拦截器是ParametersInterceptor,错误的原因是不能将value值为2015/2/1(非标准格式的日期)类型转换为register_date的数值类型。而且能提示该错误信息是因为开启了开发者模式,具体见上一天内容。
3.当转换失败的时候,如果想要让页面跳转到指定页面,则应当在配置文件中添加如下内容:
<result name="input"> <param name="location"> /01_converter/fail.jsp </param> </result>
4.struts2框架不能将在页面中得到的字符串进行类型转换时,就需要自定义类型转换器。
(1)自定义类型转换器必须实现TypeConverter接口或者继承该实现类DefaultTypeConverter,实现或者重写convertValue方法。
com.opensymphony.xwork2.conversion.TypeConverter
|--com.opensymphony.xwork.conversion.impl.DefaultTypeConverter
|--org.apache.strut2.util.StrutsTypeConverter
注意第一个接口com.opensymphony.xwork2.conversion.TypeConverter和com.opensymphony.xwork.conversion.impl.DefaultTypeConverter在xwork-core核心包,但是org.apache.strut2.util.StrutsTypeConverter在struts-core核心包。
(2)选择继承DefaultTypeConverter类,这里有三个convertValue方法,需要重写那个方法呢?
底层代码:
public abstract class DefaultTypeConverter implements TypeConverter { public Object convertValue(Map<String, Object> context, Object target, Member member,String propertyName, Object value, Class toType) { return convertValue(context, value, toType); } public Object convertValue(Map<String, Object> context, Object value, Class toType) { return convertValue(value, toType); } public Object convertValue(Object value, Class toType) { ...... } }
可见最后调用的方法是public Object convertValue(Object value, Class toType),所以重写该方法就可以了。
value是一个字符串数组,原因是struts2底层取值的方法都是getParameterValues;toType是将要转换成的数据类型。
(3)注册转换器:注册转换器的方法有两种,一种是局部的配置文件注册,另外一种是全局的配置文件配置
局部的配置文件配置方式:
在动作类的同目录下,创建一个名为"TypeConvertAction-conversion.propterties"的配置文件,在该配置文件中写上:表单中对应项下面的name属性的值=
定制转换类的全类名。如register_date=com.kdyzm.convert.DateConverter
* TypeConvertAction是Action的类名,-conversion是固定写法。
全局的配置文件方式:
在class目录下,创建一个全局的配置文件"xwork-conversion.properties",其内容配置为:java.util.Date=com.kdyzm.convert.DateConverter
这样就将所有表单中字符串类型的日期输入转换成日期对象了,而不仅仅是某个表单中的某个字段而已。
(4)自定义日期类型转换器,将yyyy年MM月dd日格式的字符串转换为标准格式的日期。
第一步:在Action类中添加属性register_date,类型是Date,提供标准的get方法和set方法。
1 package com.kdyzm.convert; 2 3 4 import java.util.Date; 5 6 import com.opensymphony.xwork2.ActionSupport; 7 8 public class TypeConvertAction extends ActionSupport{ 9 private static final long serialVersionUID = 5695452524134841455L; 10 private int id; 11 private String name; 12 private Date register_date; 13 14 public Date getRegister_date() { 15 return register_date; 16 } 17 18 public void setRegister_date(Date register_date) { 19 this.register_date = register_date; 20 } 21 22 public int getId() { 23 return id; 24 } 25 26 public void setId(int id) { 27 this.id = id; 28 } 29 30 public String getName() { 31 return name; 32 } 33 34 public void setName(String name) { 35 this.name = name; 36 } 37 38 @Override 39 public String execute() throws Exception { 40 System.out.println("访问了默认的execute方法!"); 41 System.out.println("id="+id); 42 System.out.println("name="+name); 43 System.out.println("register_date="+register_date); 44 return "success"; 45 } 46 }
第二步:创建转换器,继承DefaultTypeConverter类,并重写convertValue(Object value,Class toType)方法。
package com.kdyzm.convert; import java.io.UnsupportedEncodingException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter; public class DateConverter extends DefaultTypeConverter{ @Override public Object convertValue(Object value, Class toType) { System.out.println(value); System.out.println(toType); if(value==null||toType==null) return null; if(!toType.equals(java.util.Date.class)) return null; if(value instanceof java.lang.String[]) { String []str=(String[]) value; String temp=str[0]; try { temp=new String(temp.getBytes("iso-8859-1"),"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日"); Date date; try { date = sdf.parse(temp); return date; } catch (ParseException e) { e.printStackTrace(); } return null; } return null; } }
第三步:注册转换器,这里使用局部的配置方式,新建配置资源文件:TypeConvertAction-conversion.properties,内容如下
register_date=com.kdyzm.convert.DateConverter
第四步:验证输出
后台打印结果:
5.可以将javaBean对象作为Action类中的成员变量
但是使用方法需要改变。
首先Action文件中首先需要改变:
private Edu edu;
其中Edu是一个JavaBean。
再者jsp文件也需要改变:
学历编号:<input type="text" name="edu.eduid"><br> 学历名称:<input type="text" name="edu.eduname"><br>
6.可以将Collection对象作为Action类中的成员变量。
Collection中的成员是javaBean。
private Collection<Emp>coll;
同时jsp页面中也需要进行相应的修改:
学历编号:<input type="text" name="coll[0].eduid"><br> 学历名称:<input type="text" name="coll[0].eduname"><br> 学历编号:<input type="text" name="coll[1].eduid"><br> 学历名称:<input type="text" name="coll[1].eduname"><br>
5.配置错误提示信息为中文:乱码未解决。
怎样在jsp页面中显示中文错误提示信息呢?
应当引入sruts2中的标签。
<%@ taglib uri="/struts-tags" prefix="s"%>
使用的标签时:
<s:fielderror fieldName="createTime"/>
用法是:在fieldName字段值改为:可能会出现错误的字段的name值。
(1)当输入的日期类型不合法的时候,会提示如下信息的错误提示
控制台输出的错误信息:
该错误信息提示在两个资源文件中都都没有对应的key:invalid.fieldvalue.register_date,如果这两个资源文件中有一个存在该key值,这个错误就不会报出来了。
能不能将英文改成中文呢?
通过修改配置文件能够达到目的。
(2)配置文件所在的地方:xwork-core-2.3.24.jar包下的com.opensymphony.xwork2.xwork-messages.properties资源文件,但是jar包中的文件最好不要修改,
那么怎么做才能达到目的呢?
上面的提示信息在文件中的位置
(3)修改方法
* 在动作类Action同目录下,创建一个名为"conerter.properties"的资源文件
* 该资源文件配置如下
* 针对所有字段的:
xwork.default.invalid.field.fieldvalue=类型转换失败"{0}"
* 针对某个字段的:
invalid.fieldvalue.register_date=注册日期字符串转换成日期对象的时候失败!(对比(1)中的错误信息提示)
* 进行以上的配置之后,还应当将配置注册到struts.xml文件中。
<constant name="struts.custom.i18n.resources" value="com.kdyzm.convert.converter_messages"></constant>
注意,value值在写的时候不要加上后缀,即.properties后缀不要带上,只使用文件名就可以了。
(4)未解决的问题:struts2中的properties文件中文乱码问题,只要输入中文就会自动编码成乱码,同时,如果将文件的编码方式改成utf-8,那么再次打开的时候如果
使用myelcipse的工具打开仍然是乱码,如果是使用了properties edit工具打开的话就不会是乱码,但是在jsp文件中显示的时候仍然是乱码。很担心国际化的时候仍
然会是中文乱码,那时候国际化的时候就会完全失败了。
解决中文乱码的方法是:使用properties edit插件,但是该插件在myelcipse中并不好用。
插件里里连接地址:http://java-properties-editor.com/
二、Struts2和ServletAPI解耦
1.使用该方式的目的是什么:为了避免和ServletAPI耦合在一起,方便Action做单元测试。
2.做了什么样的改进:Struts2对HttpServletRequest、HttpSession、ServletContext进行了封装,构造了三个对象来替代这三个对象。在Struts2中可以直接使用这三个对象所对应的Map对象可以非常方便的实现对数据的增加、查找和删除。
3.使用方法简介:
(1)第一种方法:使用ServletActionContext类来获取几个对象。
1 HttpServletRequest request=ServletActionContext.getRequest(); 2 HttpServletResponse response=ServletActionContext.getResponse(); 3 HttpSession session=request.getSession(); 4 ServletContext application=ServletActionContext.getServletContext();
(2)第二种方法:实现接口以获取几个对象:ServletRequestAware,ServletResponseAware,ServletContextAware,SessionAware
这四个借口分别只定义了一个接口,实现该接口就能获取到所需要的各个对象的值。
三、国际化(.properties乱码问题没有解决)
1.软件本地化:一个软件在某个国家或者地区使用的时候采用该国家或者地区的语言、数字、货币、日期等习惯
2.软件国际化:软件开发的时候让它能够支持多个国家或者地区的本地化应用。
3.使用struts2实现软件国际化需要定义资源文件,资源文件的定义需要遵循一定的规则
资源文件会包含用默认语言编写的会在程序中出现的所有消息。这些消息会以键值对的形式存储。
当对一个应用程序进行国际化处理的时候,所要用的各种语言版本的信息应当存放在不同的资源文件中,每一个这样的文件对应着一种语言版本。所有这样的文件合在一起称为资源包(Resource Bundle)
4.属性文件的命名格式分为以下两种:
(1)文件名前缀.properties
(2)文件名前缀_语言种类.properties
文件名前缀可以自由书写,语言种类字段必须是合法的ISO语言代码。改代码可以在浏览器上查看(IE)。
文件名前缀.properties是默认的形式,当其他属性文件找不到的时候,会默认的寻找该属性文件。
5.定义不同的资源文件
(1)定义文件名:基名_国家代码_语言代码.properties,如果找不到该文件,则使用默认的文件名:基名.properties
(2)定义不同的资源文件示例:
中文的资源文件:
文件名:resource_ch_CN.properties
文件内容:item.username=张三
英文的资源文件:
文件名:resource_en_US.properties
文件内容:item.username=zhangsan
默认的资源文件:
文件名:resource.properties
文件内容:item.uername=username
6.在struts.xml文件中注册
使用的是struts.custom.i18n.resources,常量定义如下所示:
<constant name="struts.custom.i18n.resources" value="com.kdyzm.i18n.resource"></constant>
其中value值中com.kdyzm.i18n.resource中的resource是基名,并不带国家代码和语言代码,也不带后缀名。
这样一个注册语句就能够注册很多资源文件了。
7.案例:国际化小练习。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 <struts> 6 <!-- 注意在注册国际化资源文件的时候使用的value值是资源文件的基名,而不是全名,而且不使用后缀名.properties --> 7 <constant name="struts.custom.i18n.resources" value="com.kdyzm.i18n.resource"></constant> 8 <package name="i18npackage" namespace="/i18n_space" extends="struts-default"> 9 <action name="i18n_test" class="com.kdyzm.i18n.I18nAction"> 10 <result name="success"> 11 /02_i18n/success.jsp 12 </result> 13 </action> 14 </package> 15 </struts>
1 name=zhangsan
1 name=Danny
1 package com.kdyzm.i18n; 2 3 import com.opensymphony.xwork2.ActionSupport; 4 5 public class I18nAction extends ActionSupport{ 6 private static final long serialVersionUID = -7378612320977074589L; 7 8 @Override 9 public String execute() throws Exception { 10 System.out.println("访问了默认的测试方法!"); 11 return "success"; 12 } 13 }
1 <%@ page isELIgnored="false" language="java" import="java.util.*" pageEncoding="utf-8" 2 contentType="text/html; charset=utf-8" %> 3 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 4 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 5 <html> 6 <head> 7 <title>Insert title here !</title> 8 <meta http-equiv="content-type" content="text/html;charset=utf-8"> 9 </head> 10 11 <body> 12 <a href="<c:url value='/i18n_space/i18n_test.action'/>">测试</a> 13 </body> 14 </html>
1 <%@ page isELIgnored="false" language="java" import="java.util.*" pageEncoding="utf-8" 2 contentType="text/html; charset=utf-8" %> 3 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 4 <%@ taglib uri="/struts-tags" prefix="s" %> 5 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 6 <html> 7 <head> 8 <title>Insert title here !</title> 9 <meta http-equiv="content-type" content="text/html;charset=utf-8"> 10 </head> 11 12 <body> 13 i18n测试成功!<br/> 14 姓名是:<s:text name="name"></s:text> 15 </body> 16 </html>
运行结果:使用IE浏览器进行测试,首先调整语言选项为:中文(zh_CN)
然后调整为美国英文(en_US)
由此可以得到国际化小案例已经成功了!
8.配置文件中存在占位符的情况
1 name=zhangsan 2 password=zhangsan_mima 3 items={0},huan ying lai dao shandong!{1}
1 name=Danny 2 password=Danny_password 3 items={0},welcome to shandong!{1}
jsp核心代码测试区域:最关键的是<s:text>的使用方法。
<s:text name="items"> <s:param> <s:text name="name"></s:text> </s:param> <s:param> <s:text name="password"></s:text> </s:param> </s:text>
运行结果:zh_CN:
en_US:
9.TextProvider接口
(1)功能:提供对各种资源包和它们的底层文本消息的访问机制
(2)当调用该接口的getText()方法的时候,它将按照一下顺序搜索相关的属性文件。
* Action类的属性文件,该文件的名字和相关的动作类的名字相同,并且和那个动作类存放在同一个目录下。
* 动作类所实现的各个接口的属性文件
* 动作类的各个父类的属性文件
* 动作类的各个父类所实现的各个接口的属性文件
* 如果动作类实现了ModelDriven接口,Struts将会调用getModel()方法,并从模型对象的类开始沿着类的继承关系进行一次上溯搜索。
* 默认的包的属性文件
* 继承关系中的下个父包里的资源包
(3)测试在Action中的方法中调用getText方法访问资源文件中的内容
* 测试不带有占位符的getText方法
String name=this.getText("name"); System.out.println(name);
* 测试带有占位符的getText方法
String arr[]=new String[]{"张三","李四"}; String temp=this.getText("items",arr); System.out.println(temp);
运行结果:
四、解决.properties资源文件乱码问题
1.JDK自带的工具:native2ascii
使用方法:native2ascii -encoding gb2312 源文件 目标文件.properties
使用这种方法能够解决中文乱码问题,但是非常麻烦,有什么好办法能比较简单的解决这个问题吗。