第
章


高级技术

学习目的与学习要求

学习目的:掌握Struts2 国际化的思路及开发流程、
Struts2 的异常机制,了解Struts2 的类型转换器,掌握Struts 
2的校验框架。

学习要求:重点掌握Struts2 的国际化及验证框架,搭建
流程,开发该知识点的实例。

本章主要内容

本章主要讲解Struts2 框架的高级技术,包括Struts2 的
国际化、异常机制、类型转换和校验。其中国际化包括国际化
的思路及配置资源文件的种类,校验分为validate代码校验和
校验框架。

前面讲解了Struts的基本原理,接下来介绍Struts的高
级部分,包括Struts2 框架的国际化、异常处理、类型转换器和
Struts2 验证框架等。首先讲解Struts对国际化的支持。

5.国际化支持
1 

几年前,应用程序开发者能够考虑到仅支持他们本国的
只使用一种语言(有时候是两种)和通常只有一种数量表现方
式(如日期、数字、货币值)的应用。然而,基于Web技术的应
用程序的爆炸性增长,以及将这些应用程序部署在Internet或
其他被广泛访问的网络之上,已经在很多情况下使得国家的
边界淡化到不可见。这种情况转变成为一种对于应用程序支

internationalizatio18

持国际化(n,经常被称作in,因为18 是字
母i和字母n之间的字母个数)和本地化的需求。国际化是商
业系统中不可或缺的一部分,所以无论您学习的是什么Web 
框架,它都是必须掌握的技能。

Struts1.它极大地简化了程序

x对国际化有很好的支持, 


第 5 章 Struts高级技术
11 7 
员在做国际化时所需的工作。例如,如果要输出一条国际化信息,只在代码包中加入FILENAME_
xx_XX.properties(其中FILE-NAME 为默认资源文件的文件名),然后在strutsconfig.
xml中指明其路径,再在页面用<bean:message>标志输出即可。而Struts2.0在原
有的Struts1简单、易用的基础上,将其做得更灵活、更强大。
1.Struts2的国际化概述
Struts2国际化建立在Java国际化的基础之上,也是通过提供不同国家/语言环境的消息
资源,然后通过ResourceBundle加载指定Locale对应的资源文件,再取得该资源文件中指定
key对应的消息———整个过程与Java程序的国际化完全相同,只要Struts2框架对Java程序
国际化进行了进一步封装,从而简化了应用程序的国际化。
1)在Struts2中加载全局资源文件
Struts2支持4种配置和访问资源文件的方法,包括: 
. 使用全局的资源文件。
. 使用包范围的资源文件。
. 使用Action范围的资源文件。
. 使用<s:i18n>标志访问特定路径的properties文件。
其中最常用的是加载全局的国际化资源文件,至于其他几种方式,后面会讲解。加载全局
的国际化资源文件的方式通过配置常量实现。不管在struts.custom.xml文件中配置常量,还
是在struts.properties文件中配置常量,只配置struts.custom.il8n.resources常量即可。
配置struts.custom.il8n.resources 常量时,该常量的值为全局国际化资源文件的
baseName。
假如系统需要加载的国际化资源文件的baseName为properties/messageResource,则可
以在struts.properties文件中指定如下一行: 
Struts.custom.il8n.resources= properties.messageResource 
或者,在struts.xml文件中配置如下的一个常量: 
<!-- 定义资源文件的位置和类型--> 
<constant name="struts.custom.i18n.resources" value="properties/ 
messageResource "/> 
通过这种方式加载国际化资源文件后,Struts2应用就可以在所有地方取出这些国际化
资源文件了,包括JSP页面和Action。
2)访问国际化资源
Struts2既可以在JSP页面中通过标签输出国际化消息,也可以在Action类中输出国际
化消息,不管采用哪种方式,Struts2都提供了支持,使用起来非常简单。
Struts2访问国际化消息主要有如下3种方式: 
(1)为了在JSP页面中输出国际化消息,可以使用Struts2的<s:text…/>标签,该标签
可以指定一个name属性,该属性指定了国际化资源文件中的key。
(2)为了在表单元素里输出国际化信息,可以为该表单标签指定一个key属性,该key指
定了国际化资源文件中的key。
(3)为了在Action类中访问国际化消息,可以使用ActionSupport类的getText()方法, 
该方法可以接受一个name参数,该参数指定了国际化资源文件中的key。

Java高级框架应用开发与项目案例教程———Struts+Spring+Hibernate 
11 8 
举例说明。首先是提供资源文件。
#资源文件的内容就是key-value对 
loginPage=loginPage 
errorPage=errorPage 
welcomePage=welcomePage 
showBooksPage=showBooksPage 
errorTip=sorry,you login error!!! 
errorLink1=show books 
errorLink2=here coming in GetBooksAction,but you did not login,so goto login path 
welcomeTip=welcome,{0},you login success! 
welcomeLink=show books 
loginTip=Login 
user=username 
password=password 
submit=submit 
showbookTip=show books 
bookname=book name 
username.required=username is required. 
password.required=password is required. 
上面的文件以messageResource_en_US.properties文件名保存,并将其保存在WEBINF/
classes/properties路径下,然后提供如下文件: 
loginPage=登录页面
errorPage=错误页面
welcomePage=成功欢迎页面
showBooksPage=图书展现页面
errorTip=对不起,您登录失败!!! 
errorLink1=展现图书
errorLink2=(此处应进入GetBooksAction,但是检测到未登录,所以跳转到login 的path) 
welcomeTip=欢迎,{0},您已经登录成功! 
welcomeLink=展现书籍
loginTip=用户登录
user=用户名
password=密码
submit=提交
showbookTip=图书展示
bookname=书名
username.required=用户名不能为空
password.required=密码不能为空
使用native2ascii工具处理上面的资源文件,并命名为messageResource_zh_CN. 
properties保存在WEB-INF/classes/properties路径下,具体内容如下: 
loginPage=\u767b\u9646\u9875\u9762 
errorPage=\u9519\u8bef\u9875\u9762 
welcomePage=\u6210\u529f\u6b22\u8fce\u9875\u9762

第 5 章 Struts高级技术
11 9 
showBooksPage=\u56fe\u4e66\u5c55\u73b0\u9875\u9762 
errorTip=\u5bf9\u4e0d\u8d77\uff0c\u60a8\u767b\u9646\u5931\u8d25\uff01\uff01\uff01 
errorLink1=\u5c55\u73b0\u56fe\u4e66 
errorLink2=\uff08\u6b64\u5904\u5e94\u8fdb\u5165GetBooksAction\uff0c\u4f46\u662f\ 
u68c0\u6d4b\u5230\u672a\u767b\u9646\u6240\u4ee5\u8df3\u8f6c\u5230login\u7684path 
\uff09 
welcomeTip=\u6b22\u8fce\uff0c{0},\u60a8\u5df2\u7ecf\u767b\u9646\u6210\u529f\uff01 
welcomeLink=\u5c55\u73b0\u4e66\u7c4d 
loginTip=\u7528\u6237\u767b\u9646 
user=\u7528\u6237\u540d 
password=\u5bc6\u7801 
submit=\u63d0\u4ea4 
showbookTip=\u56fe\u4e66\u5c55\u793a 
bookname=\u4e66\u540d 
username.required=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a 
password.required=\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a 
提供上面两份资源文件后,系统会根据浏览者所在的区域加载对应的语言资源文件。
下面是登录页面代码: 
<%@ page language="java" contentType="text/html; charset=utf-8"%> 
<!--导入struts2 标签--> 
<%@ taglib uri="/struts-tags" prefix="s"%> 
<html> 
<head> 
<!--使用s:text 标签输出国际化消息--> 
<title><s:text name="loginPage"/></title> 
</head> 
<body> 
<h3><s:text name="loginTip"/></h3> 
<!--在表单元素中使用key 指定国际化消息的key--> 
<s:form action="Login" method="post"> 
<s:textfield name="username" key="user"/> 
<s:password name="password" key="password"/> 
<s:submit name="submit" key="submit" /> 
</s:form> 
</body> 
</html> 
上面的JSP页面中使用<s:text…/>标签直接输出国际化信息,也通过在表单元素中指
定key属性输出国际化信息。通过这种方式就可以完成JSP页面中普通文本、表单元素标签
的国际化。
如果在简体中文环境下,浏览该页面将看到如图5-1所示的页面。
如果在控制面板中修改语言/区域,将机器的语言/区域环境修改成美国英语环境,再次浏
览该页面,将看到如图5-2所示的页面。

Java高级框架应用开发与项目案例教程———Struts+Spring+Hibernate 
12 0 
图5-1 登录页面 
图5-2 修改后的登录页面
如果为了在Action 中访问国际化消息,则在Action 类中调用ActionSupport类的
getText()方法,就能够取得国际化资源文件中的国际化消息。通过这种方式,即使Action需
要设置在下一个页面显示的信息,也无须直接设置字符串常量,而是使用国际化消息的key输
出,从而实现程序的国际化。
下面是本示例应用中Action类的代码: 
package com.ascent.action; 
import com.opensymphony.xwork2.ActionContext; 
import com.opensymphony.xwork2.ActionSupport; 
@SuppressWarnings("serial") 
public class LoginAction extends ActionSupport{ 
private String username; 
private String password; 
public String getPassword() { 
return password; 
} 
public void setPassword(String password) { 
this.password = password; 
} 
public String getUsername() { 
return username; 
} 
public void setUsername(String username) { 
this.username = username; 
} 
@SuppressWarnings("unchecked") 
public String execute(){ 
if(getUsername().equals("ascent")&& getPassword().equals("ascent")){ 
ActionContext.getContext().getSession().put("user", this.getUsername()); 
return SUCCESS; 
} 
return ERROR; 
} 
//完成输入校验需要重写的validate()方法(读取资源文件getText(String str)) 
public void validate(){ 
//调用getText()方法取出国际化消息 
if(getUsername()==null||"".equals(this.getUsername().trim())){

第 5 章 Struts高级技术
12 1 
this.addFieldError("username", this.getText("username.required")); 
} 
if(this.getPassword()==null||"".equals(this.getPassword().trim())){ 
this.addFieldError("password", this.getText("password.required")); 
} 
} 
}
注:这里使用了数据验证技术,为此需要重写validate()方法。如果用户登录时没有填写
用户名或密码,系统就会报错。在简体中文环境下,提示消息分别为:用户名不能为空;密码
不能为空。如果在控制面板中修改语言/区域,将机器的语言/区域环境修改成美国英语环境, 
那么提示消息分别变为usernameisrequired;passwordisrequired。这样,也就提供了国际化
的支持。
关于数据验证的详细内容,稍后会讲解,这里不赘述了。
2.参数化国际化字符串
许多情况下,需要动态地为国际化字符插入一些参数,在Struts2.0中可以方便地做到这
一点。如
果需要在JSP页面中填充国际化消息里的占位符,则可以通过在<s:text…/>标签中
使用多个<s:param…/>标签填充消息中的占位符。第一个<s:param…/>标签指定第一
个占位符值,第二个<s:param…/>标签指定第二个占位符值,以此类推。
如果需要在Action中填充国际化消息里的占位符,则可以通过在调用getText()方法时
使用getText(StringaTextName,Listargs)或getText(Stringkey,String[]args)方法填充
占位符。该方法的第二个参数既可以是一个字符串数组,也可以是字符串组成的List对象。
在上述实例中,资源文件中有如下国际化消息: 
#带占位符的国际化消息
welcomeTip=欢迎,{0},您已经登录成功! 
为了在Action类中输出占位符的消息,我们在Action类中调用ActionSupport类的
getText()方法,调用该方法时,传入用于填充占位符的参数值。访问该带占位符消息的
Action类如下: 
package com.ascent.struts2.action; 
import com.opensymphony.xwork2.ActionContext; 
import com.opensymphony.xwork2.ActionSupport; 
@SuppressWarnings("serial") 
public class LoginAction extends ActionSupport{ 
private String username; 
private String password; 
public String getPassword() { 
return password; 
} 
public void setPassword(String password) { 
this.password = password; 
}

Java高级框架应用开发与项目案例教程———Struts+Spring+Hibernate 
12 2 
public String getUsername() { 
return username; 
} 
public void setUsername(String username) { 
this.username = username; 
} 
@SuppressWarnings("unchecked") 
public String execute(){ 
if(getUsername().equals("ascent")&& getPassword().equals("ascent")){ 
//调用getText( ) 方法取出国际化消息, 使用字符串数组传入占位符的参数值
(request 范围) 
ActionContext.getContext().put("user", this.getText("welcomeTip", new 
String[]{this.getUsername()})); 
return SUCCESS; 
} 
return ERROR; 
} 
}
通过上面的带参数的getText()方法,就可以为国际化消息的占位符传入参数了。
为了在JSP页面中输出带两个占位符的国际化消息,只为<s:text…/>标签指定<s: 
param…/>子标签即可。下面是welcome.jsp页面的代码: 
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> 
<%@ taglib prefix="s" uri="/struts-tags" %> 
<% 
String path = request.getContextPath(); 
String basePath = request. getScheme ( ) +"://" + request. getServerName ( ) +":" + 
request.getServerPort()+path+"/"; 
%> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
<head> 
<base href="<%=basePath%>"> 
<title><s:text name="welcomePage"/></title> 
</head> 
<body> 
<!--使用s:text 标签输出welcomeTip 对应的国际化消息--> 
<s:text name="welcomeTip"> 
<!--使用s:param 为国际化消息的占位符传入参数--> 
<s:param><s:property value="username"/></s:param> 
</s:text> 
<br><br> 
<!-- 输出request 范围内的user 值(资源文件取值)--> 
request:${requestScope.user} 
<br> 
<a href="getBooks.action"><s:text name="welcomeLink"/></a>

第 5 章 Struts高级技术
12 3 
</body> 
</html> 
上面的页面使用${requestScope.user}输出的是Action类中取出的国际化消息,而通过
<s:text…/>标签取出的是另一个国际化消息,且使用<param…/>标签为该国际化消息的
占位符指定了占位符值。当以ascent用户名登录成功后,结果如图5-3所示。
如果美国英语语言环境下用户通过登录页面登录成功,进入welcome.jsp页面,将看到如
图5-4所示的页面。
图5-3 中文欢迎页面图5-4 英文欢迎页面
登录失败error.jsp页面代码如下: 
<%@ page language="java" import="java.util. *" pageEncoding="UTF-8" %> 
<!--导入struts2 标签--> 
<%@ taglib uri="/struts-tags" prefix="s" %> 
<% 
String path = request.getContextPath(); 
String basePath = request.getScheme()+"://"+request.getServerName()+":"+ 
request.getServerPort()+path+"/"; 
%> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
<head> 
<base href="<%=basePath%>"> 
<!-- 使用s:text 标签输出国际化消息--> 
<title><s:text name="errorPage"/></title> 
<meta http-equiv="pragma" content="no-cache"> 
<meta http-equiv="cache-control" content="no-cache"> 
<meta http-equiv="expires" content="0"> 
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> 
<meta http-equiv="description" content="This is my page"> 
<!-- 
<link rel="stylesheet" type="text/css" href="styles.css"> 
--> 
</head> 
<body> 
<s:text name="errorTip"></s:text><br> 
<a href="login.jsp"><s:text name="errorLink1"></s:text></a> 
<s:text name="errorLink2"></s:text> 
</body> 
</html>

Java高级框架应用开发与项目案例教程———Struts+Spring+Hibernate 
12 4 
用户名或密码错误时,中文和英文环境下的页面分别如图5-5和图5-6所示。
图5-5 登录失败中文页面
图5-6 登录失败英文页面
Struts的配置文件struts.xml如下: 
<? xml version="1.0" encoding="GBK"? > 
<!DOCTYPE struts PUBLIC 
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 
"http://struts.apache.org/dtds/struts-2.0.dtd"> 
<struts> 
<constant name="struts.custom.i18n.resources" 
value="properties/messageResource " /> 
<package name="struts2" extends="struts-default"> 
<!-- 全局国际化测试类--> 
<action name="login" class="com.ascent.action.LoginAction"> 
<result>/welcome.jsp</result> 
<result name="error">/error.jsp</result> 
<result name="input">/login.jsp</result> 
</action> 
</package> 
</struts> 
3.加载其他资源文件的方式
前面介绍了Struts2中加载国际化资源最常用的方式,除此之外,Struts2还提供了多种
方式加载国际资源文件,包括指定包范围资源文件、Action范围资源文件,以及临时指定资源
文件等。
1)包范围资源文件
对于一个大型应用而言,国际化资源文件的管理也是一个复杂的工程,因为整个应用中有
大量内容需要实现国际化,如果把国际化资源都放在同一个全局文件里,这将是不可想象的事

第 5 章 Struts高级技术
12 5 
情。为了更好地体现软件工程里“分而治之”的原则,Struts2允许针对不同模块、不同Action 
组织国际化资源文件。
为Struts2应用指定包范围资源文件的方法是:在包的根路径下建立多个文件名为
package_language_country.properties的文件,一旦建立了这个系列国际化资源文件,应用中
处于该包下的所有Action都可以访问该资源文件。
例如,com.ascent.action2包下的Action类: 
package com.ascent.action2; 
import com.opensymphony.xwork2.ActionContext; 
import com.opensymphony.xwork2.ActionSupport; 
@SuppressWarnings("serial") 
public class LoginAction2 extends ActionSupport{ 
private String username; 
private String password; 
public String getPassword() { 
return password; 
} 
public void setPassword(String password) { 
this.password = password; 
} 
public String getUsername() { 
return username; 
} 
public void setUsername(String username) { 
this.username = username; 
} 
@SuppressWarnings("unchecked") 
public String execute(){ 
if(getUsername().equals("ascent")&& getPassword().equals("ascent")){ 
//调包范围资源文件 
ActionContext.getContext().put("tip", this.getText("succTip")); 
return SUCCESS; 
} 
ActionContext.getContext().put("tip", this.getText("failTip")); 
return ERROR; 
} 
}
接着提供两份资源文件:第一份资源文件为package_zh_CN.properties,内容为 
failTip=\u5305\u8303\u56f4\u6d88\u606f\uff1a\u5bf9\u4e0d\u8d77\uff0c\u60a8\u4e0d\ 
u80fd\u767b\u5f55\uff01 
succTip=\u5305\u8303\u56f4\u6d88\u606f\uff1a\u6b22\u8fce\uff0c\u60a8\u5df2\u7ecf\ 
u767b\u5f55\uff01 
它使用native2ascii工具处理以下内容: 
failTip=包范围消息:对不起,您不能登录!