第 3 章 Struts2 的高级组件 本章将介绍 Struts2 中比较常用的高级组件 通过对这些高级组件的学习, 将有助于进一步了解和使用 Struts2 框架 本章主要内容 : (1)Struts2 对国际化的支持 (2)Struts2 常用拦截器的使用 (3)Struts2 的数据验证功能 (4)Struts2 对文件上传和下载的支持 3.1 Struts2 的国际化 国际化 是指一个应用程序在运行时能够根据客户端请求所来自国家或地区语言的不同而显示不同的用户界面 例如, 请求来自于一台中文操作系统的客户端计算机, 则应用程序响应界面中的各种标签 错误提示和帮助信息时均使用中文文字 ; 如果客户端计算机采用英文操作系统, 则应用程序也应能识别并自动以英文界面做出响应 引入国际化机制的目的在于提供自适应的 更友好的用户界面, 而不必改变程序的其他功能或者业务逻辑 人们常用 I18N 这个词作为 国际化 的简称, 其来源是英文单词 Internationalization 的首末字母 I 和 N 及它们之间的字符数 18 3.1.1 Struts2 实现国际化的流程 Struts2 国际化是建立在 Java 国际化基础上的,Java 对国际化进行了优化和封装, 从而简化了国际化的实现过程 Struts2 国际化流程如图 3-1 所示 图 3-1 Struts2 国际化流程具体流程是 : (1) 不同地区使用的操作系统环境不同, 如中文操作系统 英文操作系统 韩文操作系统等 获得客户端地区的语言环境后, 在 struts.xml 文件中会找到相应的国际化资源文件, 88
如果当操作系统环境是中文语言环境, 就加载中文国际化资源文件 所以国际化需要编写支持多个语言的国际化资源文件, 并且配置 struts.xml 文件 (2) 根据选择的语言加载相应的国际化资源文件, 视图通过 Struts2 标签读取国际化资源文件把数据输出到页面上, 完成页面的显示 下面介绍在国际化流程中用到的文件 1. 国际化资源文件或者资源文件国际化资源文件又称资源文件, 是以.properties 为扩展名的文本文件, 新建一个文本文件并把扩展名改为 properties 即可 该文本文件以 健 = 值 对的形式存储的国际资源文件 例如 : key=value loginname= 用户名称 loginpassword= 用户密码 当需要多个资源文件为不同语言版本提供国际化服务时, 可以为资源文件命名, 命名的格式有以下两种形式 : 资源文件名.properties 资源文件名 _ 语言种类.properties 文件名后缀必须是.properties, 语言种类必须是有效的 ISO( 国际标准化组织 ) 语言代码,ISO-639 标准定义的这些代码格式为英文小写 双字符, 具体如表 3-1 所示 表 3-1 常用标准语言代码 语言编码语言编码 汉语 (Chinese) zh 德语 (German) de 英语 (English) en 日语 (Japanese) ja 法语 (French) fr 意大利语 (Italian) it 资源文件如果使用第一种命名方式, 即为默认语言代码, 当系统找不到与客户端请求的语言环境匹配的资源文件时, 就使用该默认的属性文件 例如, 如果要对前面介绍的登录系统进行国际化处理, 要求根据不同的语言环境显示英文和中文用户界面, 那么就需要创建英文和中文版本的资源文件, 分别取名为 globalmessages_gbk.properties 和 globalmessages_en_us.properties, 内容分别如例 3-1 和例 3-2 所示 例 3-1 中文版资源文件 (globalmessages_gbk.properties) logintitle= 用户登录 loginname= 用户名称 loginpassword= 用户密码 loginsubmit= 登录 例 3-2 英文版资源文件 (globalmessages_en_us.properties) 89
logintitle=userlogin loginname=username loginpassword=userpassword loginsubmit=login 从例 3-1 和例 3-2 中可以看出, 国际化资源文件的内容都是以 key=value 形式存在的 资源文件中 key 部分是相同的, 即等号左边部分相同,value 部分不同, 即等号右边部分不同 在国际化时, 所有的编码都要使用标准的编码方式, 需要把中文字符转换为 Unicode 代码, 否则在国际化处理时页面将会出现乱码 如例 3-1 中的中文资源文件是不能直接使用的, 必须转换为指定的编码方式 可以使用 JDK 自带的 native2ascii 工具进行中文资源文件编码方式的转换 具体操作如下 选择 开始 运行 菜单, 输入 cmd, 出现如图 3-2 所示的命令行格式界面 图 3-2 命令行窗口如果开发平台使用的是 NetBeans, 建好资源文件放在 D:\Struts2+Hibernate 框架技术教程 \ch03\src\java, 即和 struts.xml 文件一起放在默认的资源包中, 其中 Struts2+Hibernate 框架技术教程 为工作区, \ch03 为项目名, java 是资源文件存放目录 ; 如果使用的是 MyEclipse 9.1 或 Eclipse, 资源文件放在 struts.xml 文件所在的文件夹, 位置是 D:\ Struts2+Hibernate 框架技术教程 \ch03\src 在图 3-2 中进入到资源文件所在的文件夹下, 输入 :native2ascii -encoding UTF-8 globalmessages_gbk.properties globalmessages_zh_cn.properties 后, 回车, 如图 3-3 所示 图 3-3 编码转换命令 90
命令执行后在该文件夹下会生成一个名为 globalmessages_zh_cn.properties 的文件, 该文件的内容如例 3-3 所示 例 3-3 编译后的中文资源文件代码 (globalmessages_zh_cn.properties) logintitle=\u7528\u6237\u767b\u5f55 loginname=\u7528\u6237\u540d\u79f0 loginpassword=\u7528\u6237\u5bc6\u7801 loginsubmit=\u767b\u5f55 2. 在 struts.xml 文件中配置编写完国际化资源文件后, 需要在 struts.xml 文件中配置国际化资源文件的名称, 从而使 Struts2 的 I18N 拦截器在加载国际化资源文件的时候能找到这些国际化资源文件, 在 struts.xml 中的配置很简单, 代码如例 3-4 所示 例 3-4 在 struts.xml 中配置资源文件 <struts> <!-- 使用 Struts2 中的 I18N 拦截器, 并通过 constant 元素配置常量, 指定国际资源文件名字, value 的值就是常量值, 即国际化资源文件的名字 --> <constant name="struts.custom.i18n.resources" value="globalmessages" /> <constant name="struts.i18n.encoding" value="utf-8" /> <package name="i18n" extends="struts-default"> <action name="checklogin" class="loginaction.loginaction"> <result name="success">/i18n/loginsuccess.jsp</result> <result name="error">/i18n/login.jsp</result> </action> </package> </struts> 3. 输出国际化信息国际化资源文件中的 value 值是要根据语言环境通过 Struts2 标签输出到页面上的, 可以在页面上输出国际化信息, 也可以在表单的 label 标签上输出国际化信息 3.1.2 Struts2 国际化应用实例 本实例开发一个中英文登录页面, 通过该项目的开发可以帮助我们了解 Struts2 国际化应用的实现过程 1. 项目介绍该项目为中英文登录系统, 项目有一个登录页面 (login.jsp), 代码如例 3-5 所示 ; 对应的资源文件如例 3-1 和例 3-2 所示 ; 登录页面对应的业务控制器是 LoginAction 类, 代码如例 3-7 所示 ; 如果登录成功 ( 用户名 密码正确 ) 转到 loginsuccess.jsp 页面, 代码如例 3-6 所示 ; 如果登录失败 ( 用户名 密码不正确 ) 则重新回到登录页面 (login.jsp) 此外还需要配置 web.xml, 代码如例 1-3 所示 ; 配置 struts.xml, 代码如例 3-4 所示 项目的文件结构如图 3-4 所示 91
图 3-4 项目文件结构 2. 在 web.xml 中配置核心控制器 FilterDispatcher 参照 1.3.1 节中的例 1-3 3. 编写国际化资源文件并进行编码转换编写中 英文国际化资源文件, 参考例 3-1 和例 3-2; 编码转换参考图 3-3 4. 编写视图组件 (JSP 页面 ) 输出国际化消息编写一个如图 3-5 或者图 3-6 所示的登录页面 图 3-5 中文登录页面 图 3-6 英文登录页面 92
登录页面是一个 JSP 页面, 代码如例 3-5 所示 例 3-5 中英文登录页面 (login.jsp) <%@ page contenttype="text/html; charset=utf-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <!-- 使用 text 标签输出国际化消息 --> <title><s:text name="logintitle"/></title> </head> <body> <s:form action="checklogin" method="post"> <!-- 表单元素的 key 值与资源文件的 key 对应 --> <s:textfield name="name" key="loginname" size="20"/> <s:password name="password" key="loginpassword" size="22"/> <s:submit key="loginsubmit"/> </s:form> </body> </html> 登录成功页面, 代码如例 3-6 所示 例 3-6 登录成功页面 (loginsuccess.jsp) <%@ page contenttype="text/html; charset=utf-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <!-- 使用 text 标签输出国际化消息 --> <title><s:text name="successpage"/></title> </head> <body> <hr> <s:text name="loginname"/>:<s:property value="name"/><br> <s:text name="loginpassword"/>:<s:property value="password"/> </body> </html> 5. 编写业务控制器 Action login.jsp 对应的业务控制器是如例 3-7 所示的 LoginAction 类 例 3-7 中英文登录页面对应的业务控制器 (LoginAction.java) package loginaction; import com.opensymphony.xwork2.actioncontext; import com.opensymphony.xwork2.actionsupport; public class LoginAction extends ActionSupport{ private String name; 93
private String password; // 用于定义标题信息 private String tip; public String getname() { return name; public void setname(string name) { this.name = name; public String getpassword() { return password; public void setpassword(string password) { this.password = password; public String gettip() { return tip; public void settip(string tip) { this.tip = tip; public String execute() throws Exception { if (getname().equals("qq")&&getpassword().equals("123") ) { ActionContext.getContext().getSession().put("name",getName()); return SUCCESS; else { return ERROR; 6. 在 struts.xml 中配置 Action 与国际资源文件修改配置文件 struts.xml, 在配置文件中配置 Action 和国际化资源文件, 参考例 3-4 7. 项目部署和运行项目部署后运行, 如果操作系统是中文系统, 运行 login.jsp 后, 出现如图 3-5 所示的页面 在图 3-5 用户名称 中输入 QQ 用户密码 中输入 123 后, 单击 登录 按钮, 登录成功, 页面如图 3-7 所示 如果使用的是英文操作系统或者通过设置浏览器语言, 运行后将出现如图 3-6 所示的英文登录页面 把浏览器设置为英文语言的步骤如下 : (1) 选择 IE 浏览器 工具 Internet 选项, 如图 3-8 所示 94
图 3-7 中文语言系统下的登录成功页面 图 3-8 选择 Internet 选项 (2) 选择图 3-8 中的 Internet 选项, 出现如图 3-9 所示的对话框 (3) 单击图 3-9 中的 语言 按钮, 出现如图 3-10 的对话框 在图 3-10 对话框中, 语言 区域默认的是 中文 ( 中国 )[zh-cn], 即在中文操作系统下,IE 浏览器默认的首选语言是中文, 也可以添加其他语言 图 3-9 Internet 选项 对话框 图 3-10 语言首选项 对话框 (4) 单击图 3-10 中的 添加 按钮, 弹出 添加语言 对话框, 找到 英语 ( 美国 ) [en-us] 项, 如图 3-11 所示 (5) 在图 3-11 中选定所需的语言后单击 确定 按钮, 返回到 语言首选项 对话框, 95
如图 3-12 所示, 该对话框中的语言区域中已添加了 英语 ( 美国 )[en-us], 选定 英语 ( 美国 )[en-us] 项, 单击 上移 按钮, 把英语作为浏览器的首选语言, 最后单击 确定 按钮完成设置 图 3-11 添加语言 对话框 图 3-12 语言首选项 对话框 经过上面几步配置后, 浏览器的首选语言已是 英语, 浏览器在运行 JSP 页面 (login.jsp) 时, 将优先使用 英语, 如果有英文国际化资源文件, 将显示英文版的登录页面, 如图 3-6 所示 在图 3-6 的 UserName 中输入 QQ, 并在 UserPassword 中输入 123 后, 单击 Login 按钮, 登录成功, 页面如图 3-13 所示 图 3-13 英文版的登录成功页面 3.2 Struts2 的拦截器 拦截器是 Struts2 的核心组件,Struts2 的绝大部分功能都是通过拦截器来完成, 所以 Struts2 的很多功能都构建在拦截器的基础上 3.2.1 Struts2 拦截器的基础知识拦截器 (Interceptor) 体系是 Struts2 的一个重要组成部分, 正是大量的内置拦截器实现了 Struts2 的大部分操作 当 FilterDispatcher 拦截到用户请求后, 大量的拦截器将会对用户请求进行处理, 然后 96
才调用用户自定义的 Action 类中的方法来处理请求 比如 params 拦截器将 HTTP 请求中的参数解析出来, 并将这些解析出来的参数设置为 Action 的属性 ;servlet-config 拦截器直接将 HTTP 请求中的 HttpServletRequest 实例和 HttpServletResponse 实例传给 Action; 国际化拦截器 I18N 对国际化资源进行操作 ; 文件上传拦截器 fileupload 将文件信息传给 Action 另外还有数据校验拦截器对数据校验信息进行拦截 对于 Struts2 的拦截器体系而言, 当需要使用某个拦截器时, 只需在配置文件 struts.xml 中配置就可以使用 ; 如果不需要使用该拦截器, 只需在 struts.xml 配置文件中取消配置即可 Struts2 的拦截器可以理解为一种可插拔式的设计思想, 所以 Struts2 框架具有非常好的可扩展性 拦截器实现了 AOP(Aspect-Oriented Programming, 面向切面编程 ) 的设计思想, 拦截是 AOP 的一种实现策略 AOP 是目前软件开发中的一个热点, 也是 Spring 框架中的一个重要内容 利用 AOP 可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低, 提高程序的可重用性, 同时提高开发的效率 3.2.2 Struts2 拦截器实现类 在项目开发中,Struts2 内置的拦截器可以完成项目的大部分功能, 但有些与系统逻辑相关的通用功能则需要通过自定义拦截器来实现, 如权限控制和用户输入内容的控制等 自定义拦截器需要实现 Struts2 提供的 Interceptor 接口 通过实现该接口可以开发一个拦截器类 该接口的代码如例 3-8 所示 例 3-8 Struts2 的拦截器接口 (Interceptor.java) import com.opensymphony.xwork2.actioninvocation; import java.io.serializable; public interface Interceptor extends Serializable { void destroy(); void init(); String intercept(actioninvocation invocation) throws Exception; 该接口提供了 3 个方法 : (1)void destroy() 方法 : 与 init() 对应, 用于在拦截器执行完之后释放 init() 方法里打开的资源 (2)void init() 方法 : 由拦截器在执行之前调用, 主要用于初始化系统资源, 如打开数据库资源等 (3)intercept(ActionInvocation invocation) 方法 : 该方法是拦截器的核心方法, 实现具体的拦截操作, 返回一个字符串作为逻辑视图 与 Action 一样, 如果拦截器能够成功调用 Action, 则 Action 中的 execute() 方法返回一个字符串类型值, 作为逻辑视图, 否则, 返回开发者自定义的逻辑视图 在 Java 语言中, 有时候通过一个抽象类来实现一个接口, 在抽象类中提供该接口的空 97