第 章 高级技术 学习目的与学习要求 学习目的:掌握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=包范围消息:对不起,您不能登录!