Struts2中的信息国际化
1.国际化原理?什么是国际化?
同一款软件,可以为不同用户,提供不同的语言界面(国际化软件)
需要一个语言资源包(很多properties文件,每个properties文件 针对一个国家或一种语言,通过Java程序根据来访者国家语言,自动读取不同的properties文件)
2.资源包编写
properties文件命名:基本名称_语言(小写)_国家(大写).properties
例如:
message_zh_CN.properties 中国中文
message_en_US.properties 美国英文
3.ResourceBundle 根据不同Locale(地域信息),读取不同国家properties文件
ResourceBundle bundle=ResourceBundle.getBundle("message",Locale.US);
Struts2中的信息国际化:
Struts2中对国际化进行了封装,我们只需要根据其提供的API进行访问就可以。
Struts2国际化配置与使用
在Struts2中国际化时properties文件的定义方式:
(1)全局(所有Action都可以使用)
这种方式也最常用,对于properties配置文件可以放在任何包中,需要在Struts中进行配置
需要引用Struts2 中core下的org.apache.struts2包中default.properties中的一个常量
struts.custom.i18n.resources=testmessages,testmessages2
在Struts2中配置默认国际化常量如下:
<constant name="struts.custom.i18n.resources" value="message"> 代表message.properties在src下
<constant name="struts.custom.i18n.resources" value="cn.itcast.i18n.resource.message"> 代表message.properties在cn.itcast.i18n.resource包下.
(2)Action范围信息文件(只能在某个Action中使用)
数据只能在对应Action中使用,配置文件位置:与Action在同一包下 名称:Action类名.properties
无需在struts.xml中进行额外配置。
(3)package范围信息文件(package中所有Action都可以使用)
数据对包(包括子包)中的所有Action都有效,在指定的包中创建package.properties
(4)临时信息文件(主要在jsp中引入)
<s:i18n name="cn.itcast.action.package"></s:i18n>
一般在哪些位置上使用Struts2国际化操作?
- Action类中使用
- 配置文件中使用 例如:xxx-validation.xml
- 在jsp页面上使用
怎样在Struts2中操作国际化?
- 第一种:在Action类中使用。前提是Action类要继承自ActionSupport类。可以通过getText(String name)就可以获取配置文件中对应名称的值。
- 第二种:在validation.xml文件中使用。例如:<message key="名称"/>
- 第三种:在JSP页面上使用。<s:text name="名称"> 如果没有使用<s:i18n name="">来指定,会从全局配置文件中获取。如果要从一个配置文件中获取,通过name属性来指定包名.配置文件名
在Struts2国际化配置中使用动态文本
(1)在Action中使用动态文本
配置文件中:msg=hello world {0} 在action类中this.getText("msg", new String[]{"tom"})结果就是:hello world tom
(2)JSP页面上怎样使用
配置文件中:msg=hello world {0}
在JSP中
<s:i18n name="cn.itcast.action.I18nDemo1Action">
<s:text name="msg">
<s:param>张三</s:param>
</s:text>
</s:i18n>
<s:text name="msg">
<s:param>张三</s:param>
</s:text>
</s:i18n>
结果就是hello world
程序实例如下:
I18nDemo1Action(测试action获取国际化资源内容)
package cn.itcast.action;
import com.opensymphony.xwork2.ActionSupport;
public class I18nDemo1Action extends ActionSupport {
@Override
public String execute() throws Exception {
// 得到properties文件中的信息
System.out.println(this.getText("msg"));
// 动态文本
System.out.println(this.getText("msg", new String[] { "tom" }));
return NONE;
}
}
I18nDemo2Action(测试validation.xml中获取资源文件内容)
package cn.itcast.action;
import com.opensymphony.xwork2.ActionSupport;
public class I18nDemo2Action extends ActionSupport {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String execute() throws Exception {
return NONE;
}
}
I18nDemo1Action.properties (action范围的资源文件 针对I18nDemo1Action)msg=hello world {0}
I18nDemo2Action-validation.xml(I18nDemo2Action的配置校验文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
<message key="nameerror"></message>
</field-validator>
</field>
</validators>
package_en_US.properties(指定美国英语格式 当前包内范围内使用)
nameerror=name required
package_zh_CN.properties (指定中国英文格式 当前包范围内使用)
package.properties(当找不到指定的Locale指定的资源文件时 就从这个默认的资源文件中取)
nameerror=name required
在cn.itcast.i18n.resource资源包下创建全局的资源文件
message.properties
name=tom
message_en_US.properties
name=tom
message_zh_CN.properties
配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 声明一个全局的国际化文件 -->
<constant name="struts.custom.i18n.resources" value="cn.itcast.i18n.resource.message"></constant>
<package name="default" namespace="/" extends="struts-default">
<action name="i18ndemo1" class="cn.itcast.action.I18nDemo1Action"></action>
<action name="i18ndemo2" class="cn.itcast.action.I18nDemo2Action">
<result name="input">/input.jsp</result>
</action>
</package>
</struts>
i18n.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<s:i18n name="cn.itcast.action.package">
<s:text name="nameerror"></s:text>
</s:i18n>
<br>
<s:i18n name="cn.itcast.action.I18nDemo1Action">
<s:text name="msg">
<s:param>张三</s:param>
</s:text>
</s:i18n>
<br>
<s:text name="name" />
</body>
</html>
input.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<s:fielderror />
</body>
</html>
首先测试jsp上获取资源文件内容:
测试配置文件获取资源文件内容
测试Action中获取资源文件内容
Struts2拦截器
Struts2拦截器在访问某个Action方法之前或之后实施拦截。
Struts2拦截器是可插拔的,拦截器是AOP(面向切面编程的一种实现)
拦截器采用责任链模式,在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成的一条链。责任链里的每一个节点都可以继续调用下一个节点,也可以阻值流程继续执行。
在Struts2中可以定义很多个拦截器,将多个拦截器按照特定顺序,组成拦截器栈(顺序调用栈中的每一个拦截器)
Struts2执行原理—底层分析
Struts2的工作机制:
一个请求在Struts2框架中的处理大概分为以下几个步骤:
1、客户端初始化一个指向Servlet容器(例如Tomcat)的请求。
2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action。注意的是:FilterDispatcher类已经过时,早已被StrutsPrepareAndExecuteFilter
4、如果ActionMapper决定需要调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy。
5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类。
6、ActionProxy创建一个ActionInvocation的实例。
7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Interceptor)的调用。
8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模板。在表示的过程中可以使用Struts2框架中继承的标签。在这个过程中需要涉及到ActionMapper。
Interceptor接口
每个拦截器都是实现了com.opensymphony.xwork2.interceptor.Interceptor接口的Java类
此接口的源代码(已删除注释)如下:
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
init:该方法将在拦截器被创建后立即被调用,它在拦截器的生命周期内只被调用一次,可以在该方法中对相关资源进行必要的初始化。
interceptor:每拦截一个动作请求,该方法就会被调用一次。
destroy:该方法将在拦截器被销毁之前被调用,它在拦截器的生命周期内也只被调用一次。
注意:
- Struts 会依次调用程序员为某个 Action 而注册的每一个拦截器的 interecept 方法.
- 每次调用 interecept 方法时, Struts 会传递一个ActionInvocation接口的实例.
- ActionInvocation: 代表一个给定动作的执行状态, 拦截器可以从该类的对象里获得与该动作相关联的 Action 对象和 Result 对象. 在完成拦截器自己的任务之后, 拦截器将调用 ActionInvocation 对象的 invoke 方法前进到 Action 处理流程的下一个环节.
- 还可以调用 ActionInvocation 对象的 addPreResultListener 方法给 ActionInvocation 对象 “挂” 上一个或多个 PreResultListener 监听器. 该监听器对象可以在动作执行完毕之后, 开始执行动作结果之前做些事情
- AbstractInterceptor类实现了 Interceptor 接口. 并为 init, destroy 提供了一个空白的实现
- Alias Interceptor 名字:alias说明:在不同请求之间将参数在不同名字间转换,请求内容不变。
- Chaining Interceptor 名字:chain 说明:让前一个Action的属性可以被后一个Action访问,现在和chain类型的result()结合使用。
- Checkbox Interceptor 名字:checkbox说明:添加了checkbox自动处理代码,将没有选中checkbox的内容设置为false,而HTML默认情况下不提交没有选中的checkbox。
- Cookies Interceptor 名字:cookies说明:使用配置的name,value是指cookies。
- Conversion Error Interceptor 名字:conversionError说明:将错误从ActionContext中添加到Action的属性字段中。
- Create Session Interceptor 名字:createSession 说明:自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。
- Debugging Interceptor 名字:debugging说明:提供不同的调试用的页面来展现内部的数据状况。
- Execute and wait Interceptor 名字:execAndWait说明:在后台执行Action,同时将用户带到一个中间的等待页面。
- Exception Interceptor 名字:exception说明:将异常定位到一个画面。
- File Upload Interceptor 名字:fileUpload说明:提供文件上传功能。
- I18n Interceptor 名字:i18n说明:记录用户选择的locale。
- Logger Interceptor 名字:logger说明:输出Action的名字。
- Message Store Interceptor 名字:store说明:存储或者访问实现ValidationAware接口的Action出现的消息,错误,字段错误等。
- Model DrivenInterceptor 名字:model-driven说明:如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。
- Scoped Model Driven 名字:scoped-model-driven 说明:如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model 调用Action的setModel方法将其放入Action内部。
- Parameters Interceptor 名字:params说明:将请求中的参数设置到Action中。
- Prepare Interceptor 名字:prepare说明:如果Action实现了Preparable,则该拦截器调用Action类的prepare方法。
- Scope Interceptor 名字:scope 说明:将Action状态存入session和application的简单方法。
- Servlet Config Interceptor名字:servletConfig说明:提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。
- Static Parameters Interceptor名字:staticParams说明:从struts.xml文件中将中的中的内容设置到对应的Action中。
- Roles Interceptor 名字:roles 说明:确定用户是否具有JAAS指定的Role,否则不予执行。
- Timer Interceptor 名字:timer 说明:输出Action执行的时间
- Token Interceptor 名字:token 说明:通过Token来避免双击
- Token Session Interceptor 名字:tokenSession 说明:和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中
- Validation Interceptor 名字:validation 说明:使用action-validation.xml文件中定义的内容校验提交的数据。
- Workflow Interceptor 名字:workflow 说明:调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面
- Parameter Filter Interceptor 名字:N/A 说明:从参数列表中删除不必要的参数
- Profiling Interceptorprofiling 名字:profiling说明:通过参数激活profile
自定义拦截器
使用拦截器的用途:可以通过使用拦截器进行控制action的访问。例如权限操作。
自定义拦截器的步骤:
步骤一:.创建一个Interceptor 可以自定义一个类实现com.opensymphony.xwork2.interceptor.Interceptor
在这个接口中有三个方法 init destory intercept, intercept方法是真正拦截的方法。
在intercept方法中如果要向下继续执行,通过其参数ActionInvocation调用它的invoke()方法就可以。如下图:
在这个接口中有三个方法 init destory intercept, intercept方法是真正拦截的方法。
在intercept方法中如果要向下继续执行,通过其参数ActionInvocation调用它的invoke()方法就可以。如下图:
public class PermissionInterceptor implements Interceptor {
private static final long serialVersionUID = -5178310397732210602L;
public void destroy() {
}
public void init() {
}
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("进入拦截器");
if(session里存在用户){
String result = invocation.invoke();
}else{
return “logon”;
}
//System.out.println("返回值:"+ result);
//return result;
}
}
步骤二:.声明一个Interceptor
在struts-default.xml文件中
<interceptors>
<interceptor name="" class=""/>
</interceptors>
注意:我们要自己声明一个interceptor可以在struts.xml文件中声明。
<interceptors>
<interceptor name="" class=""/>
</interceptors>
注意:我们要自己声明一个interceptor可以在struts.xml文件中声明。
步骤三:.在action中指定使用哪些拦截器.
<interceptor-ref name="my"/>
注意:只要显示声明使用了一个拦截器。那么默认的拦截器就不在加载。为了继续不影响加载原来默认的可以这样配置struts.xml
<interceptor-ref name="my"/>
注意:只要显示声明使用了一个拦截器。那么默认的拦截器就不在加载。为了继续不影响加载原来默认的可以这样配置struts.xml
<package name="itcast" namespace="/test" extends="struts-default">
<interceptors>
<interceptor name=“permission" class="cn.itcast.aop.PermissionInterceptor" />
<interceptor-stack name="permissionStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name=" permission " />
</interceptor-stack>
</interceptors>
<action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">
<result name="success">/WEB-INF/page/hello.jsp</result>
<interceptor-ref name="permissionStack"/>
</action>
</package>
如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-ref name="permissionStack"/>把拦截器定义为默认拦截器。注意:每个包只能指定一个默认拦截器。另外,一旦我们为该包中的某个action显式指定的了某个默认拦截器,则默认拦截器不会起作用。
如果对于一个Action类中有多个请求处理方法,要求对有的请求处理进行权限验证,有的不需要登录(验证权限)
则不用直接实现Interceptor接口,而是可以让Action类继承其实现类AbstractInterceptor的子类MethodFilterInterceptor

它有以下属性
有如下方法:
includeMethods表示进行拦截的方法
excludeMethods 表示不进行拦截的方法
一般在struts.xml中配置,并且一般只写一种 例如只配置includeMethods 时 除了包含的方法外别的方法都不会再进行拦截如果只配置excludeMethods 则除了不拦截的方法外,别的方法都拦截。
例如配置方法示例如下:
<interceptors>
<intercept name="" class="">
<param name="includeMethods">add,update,delete</param>
<param name="excludeMethods">search</param>
</intercept>
</interceptors>
<intercept name="" class="">
<param name="includeMethods">add,update,delete</param>
<param name="excludeMethods">search</param>
</intercept>
</interceptors>
而且Action继承这个MethodFilterInterceptor类,必须重写doInterceptor抽象方法
自定义拦截器示例一:Action实现Interceptor接口
创建一个Action对象 Demo1Action
package cn.itcast.action;
import com.opensymphony.xwork2.ActionSupport;
public class Demo1Action extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("Demo1Action ......");
return SUCCESS;
}
}
创建两个拦截器分别放行和拦截后跳转
MyInterceptor
package cn.itcast.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class MyInterceptor implements Interceptor {
@Override
public void destroy() {
}
@Override
public void init() {
System.out.println("my interceptor init");
}
@Override
public String intercept(ActionInvocation action) throws Exception {
System.out.println("my interceptor 拦截......");
// 放行
return action.invoke();
}
}
MyInterceptor2
package cn.itcast.interceptor;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class MyInterceptor2 implements Interceptor {
@Override
public void destroy() {
}
@Override
public void init() {
System.out.println("my interceptor init");
}
@Override
public String intercept(ActionInvocation action) throws Exception {
System.out.println("my interceptor2 拦截......");
// 放行
// return action.invoke();
return Action.LOGIN;
}
}
在struts.xml中配置如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="interceptor1" class="cn.itcast.interceptor.MyInterceptor" />
<interceptor name="interceptor2" class="cn.itcast.interceptor.MyInterceptor2" />
<interceptor-stack name="customStack">
<interceptor-ref name="interceptor2" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="interceptor1"/>
<action name="demo1" class="cn.itcast.action.Demo1Action">
<result>/success.jsp</result>
<result name="login">/input.jsp</result>
</action>
<action name="demo2" class="cn.itcast.action.Demo1Action">
<result>/success.jsp</result>
<result name="login">/input.jsp</result>
<interceptor-ref name="customStack" />
</action>
</package>
</struts>
input.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<h1>被拦截</h1>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<h1>成功访问</h1>
</body>
</html>
部署到服务器 并测试结果如下:
访问/demo1如下所示:
访问/demo2....

自定义拦截器示例二—继承MethodFilterInterceptor 实现权限控制
1.login.jsp------>LoginAction------------->book.jsp
登录成功,将用户存储到session。
2.在book.jsp中提供crud链接。
每一个连接访问一个BookAction中一个方法。
登录成功,将用户存储到session。
2.在book.jsp中提供crud链接。
每一个连接访问一个BookAction中一个方法。
要求:对于BookAction中的add,update,delete方法要求用户必须登录后才可以访问。search无要求。
怎样解决只控制action中某些方法的拦截?
首先,创建类不在实现Interceptor接口,而是继承其下的一个子类.MethodFilterInterceptor,不用在重写intercept方法,而是重写 doIntercept方法。
其次.在struts.xml文件中声明
<interceptors>
<intercept name="" class="">
<param name="includeMethods">add,update,delete</param>
<param name="excludeMethods">search</param>
</intercept>
</interceptors>
新建Demo 示例代码如下:
User
package cn.itcast.domain;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
LoginAction
package cn.itcast.action;
import org.apache.struts2.ServletActionContext;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class LoginAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
@Override
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
if ("tom".equals(user.getUsername())
&& "123".equals(user.getPassword())) {
ServletActionContext.getRequest().getSession()
.setAttribute("user", user);
return SUCCESS;
} else {
this.addActionError("用户名或密码错误");
return INPUT;
}
}
}
新建BookAction
package cn.itcast.action;
import com.opensymphony.xwork2.ActionSupport;
public class BookAction extends ActionSupport {
public String add() throws Exception {
System.out.println("book action add");
return null;
}
public String update() throws Exception {
System.out.println("book action update");
return null;
}
public String delete() throws Exception {
System.out.println("book action delete");
return null;
}
public String search() throws Exception {
System.out.println("book action search");
return null;
}
}
新建自定义拦截器类MethodFilterInterceptor
package cn.itcast.interceptor;
import org.apache.struts2.ServletActionContext;
import cn.itcast.action.BookAction;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class BookInterceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
// 1.得到session中的user
User user = (User) ServletActionContext.getRequest().getSession()
.getAttribute("user");
if (user == null) {
Object obj = invocation.getAction(); // 得到当前拦截器对象
if (obj instanceof BookAction) {
BookAction action = (BookAction) obj;
action.addActionError("权限不足,请先登录"); // 存储错误信息
return Action.LOGIN;
} else {
return invocation.invoke();
}
}
return invocation.invoke();
}
}
配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="bookInterceptor" class="cn.itcast.interceptor.BookInterceptor">
<param name="includeMethods">add,update,delete</param>
</interceptor>
<interceptor-stack name="myStack">
<interceptor-ref name="bookInterceptor" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<action name="login" class="cn.itcast.action.LoginAction">
<result name="input">/login.jsp</result>
<result>/book.jsp</result>
</action>
<action name="book_*" class="cn.itcast.action.BookAction"
method="{1}">
<result name="login">/login.jsp</result>
<interceptor-ref name="myStack" />
</action>
</package>
</struts>
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<s:fielderror />
<s:actionerror />
<form action="${pageContext.request.contextPath}/login" method="POST">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br> <input
type="submit" value="login">
</form>
</body>
</html>
book.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/book_add">book add</a><br>
<a href="${pageContext.request.contextPath}/book_update">book update</a><br>
<a href="${pageContext.request.contextPath}/book_delete">book delete</a><br>
<a href="${pageContext.request.contextPath}/book_search">book search</a><br>
</body>
</html>
运行结果如下:
删除浏览器记录后再次访问
剖析拦截器的执行过程:
源代码执行流程:
1、在StrutsPrepareAndExecuteFilter中查找在doFilter方法内有一句话 execute.executeAction (request, response, mapping) 执行Action操作
2、在executeAction执行过程中会访问Dispatcher类中的serviceAction,在这个方法中会创建一个
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false); 这就是我们的Action的代理对象。
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false); 这就是我们的Action的代理对象。
3、查看ActionInvocation,查看其实现类DefaultActionInvocation,在其invoke方法中有如下一段代码:
if (interceptors.hasNext()) {//判断是否有下一个拦截器.
final InterceptorMapping interceptor = interceptors.next(); //得到一个拦截器
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
//调用得到的拦截器的拦截方法.将本类对象传递到了拦截器中。
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
}
通过源代码分析,发现在DefaultActionInvocation中就是通过递归完成所有的拦截调用操作.如下图所示
Interceptor与Filter的区别:
1、拦截器是基于Java反射机制的,而过滤器是基于函数回调的
2、过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能
5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次
Struts2中文件上传与下载
文件上传
文件上传概述
- 要想使用HTML表单上传一个或多个文件,必须把HTML表单的encType属性设置为multipart/form-data, 把它的method属性设置为post
- 为了让用户能够选择一个文件进行上传,程序员必须提供一个<input type="file">字段
- 关于服务器端使用Commons-fileupload组件(核心类:DiskFileItemFactory、ServletFileUpload、FileItem)
Struts2中文件上传
默认情况下Struts2框架使用的就是commons-fileupload
Struts2它使用了一个interceptor帮助我们完成文件上传操作。
Struts2它使用了一个interceptor帮助我们完成文件上传操作。
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
步骤:
1.在JSP页面的文件上传表单里使用file标签。如果一次上传多个文件就必须使用多个标签,但是它们的名字是相同的。
2.在Action中新添加3个和文件上传有关的属性这三个属性的名字必须是以下格式
(关于属性设置示例 可以查看FileUploadInterceptor拦截器类中的文档注释)
private File uploadImage; //上传的文件名(与表单中上传组件的名称一致)
private String uploadImageContentType; // 上传文件的类型
private String uploadImageFileName; //上传文件的名称
注意:
uploadImage是JSP页面上的file标签的名字
上传文件:<input type="file" name="
uploadImage
">
如果是上传单个组件,uploadImage属性的类型就是java.io.File,它代表被上传的文件,第二个属性和第三个属性的类型是String,它们分别代表上传文件的文件名和文件类型。定义的方式分别是:
- JSP页面file组件的名称+ContentType,
- JSP页面file组件的名称+FileName
如果上传多个文件,可以使用数组或List
在Action的execute的方法中使用common-io包下的FileUtils完成文件复制
单文件上传代码如下:
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。这两个文件可以从http://commons.apache.org/下载
第二步:把form表的encType设置为: "multipart/form-data",如下:
<form action="${pageContext.request.contextPath}/upload/uploadAction_saveFile.action"
name="form1" method="post" enctype="multipart/form-data" >
上传文件名称:<input type="file" name="uploadImage">
<input type="submit" value="上传">
</form>
name="form1" method="post" enctype="multipart/form-data" >
上传文件名称:<input type="file" name="uploadImage">
<input type="submit" value="上传">
</form>
第三步:在Action类中添加以下属性。属性红色部分对应于表单中文件字段的名称:
public class UploadAction{
private File uploadImage;//得到上传的文件
private String uploadImageContentType;//得到文件的类型
private String uploadImageFileName;//得到文件的名称
//这里略省了属性的getter/setter方法
public String saveFile() throws Exception{
ServletContext sc = ServletActionContext.getServletContext();
String realpath = sc.getRealPath("/uploadfile");
try {
File destFile = new File(realpath, uploadImageFileName);
FileUtils.copyFile(uploadImage, destFile);
} catch (IOException e) {
e.printStackTrace();
}
return "success";
private File uploadImage;//得到上传的文件
private String uploadImageContentType;//得到文件的类型
private String uploadImageFileName;//得到文件的名称
//这里略省了属性的getter/setter方法
public String saveFile() throws Exception{
ServletContext sc = ServletActionContext.getServletContext();
String realpath = sc.getRealPath("/uploadfile");
try {
File destFile = new File(realpath, uploadImageFileName);
FileUtils.copyFile(uploadImage, destFile);
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
}
最后定义上传出错要转向的页面
第四步:在struts.xml文件中增加如下配置
<package name="upload" namespace="/upload" extends="struts-default" >
<!-- 单文件上传 -->
<action name="uploadAction_*" class="cn.itcast.upload.UploadAction" method="{1}">
<result name="success">/upload/success.jsp</result>
<!-- 定义上传出错要转向的页面 -->
<result name="input">/upload/error.jsp</result>
</action>
<package>
<!-- 单文件上传 -->
<action name="uploadAction_*" class="cn.itcast.upload.UploadAction" method="{1}">
<result name="success">/upload/success.jsp</result>
<!-- 定义上传出错要转向的页面 -->
<result name="input">/upload/error.jsp</result>
</action>
<package>
Struts2中文件上传的细节:
1、关于控制文件上传大小
在struts-core-2.3.3.jar包下org\apache\struts2\default.properties 中定义了文件上传大小
struts.multipart.maxSize=2097152 上传文件默认的大小2M,
可以在struts.xml中重新设置大小
设置方法:在struts.xml文件加载该资源文件
<!-- 设置comonFileupload上传组件允许的文件大小 才能测试出上传文件过大出现的错误信息默认值是2M-->
<constant name="struts.multipart.maxSize" value="86170804"/>
<constant name="struts.multipart.maxSize" value="86170804"/>
2、在Struts2中默认使用的是Commons-fileupload进行文件上传
# struts.multipart.parser=cos
# struts.multipart.parser=pell
struts.multipart.parser=jakarta
# struts.multipart.parser=pell
struts.multipart.parser=jakarta
如果使用pell,cos进行文件上传,必须导入其jar包
3、如果出现问题,需要配置input视图,在页面上可以通过<s: fielderror>展示错误信息, 即在struts.xml文件中根据<result name=“input”>/upload/error.jsp</result>中所指向的error.jsp页面可以使用<s:fielderror/>显示错误信息
查看错误信息的资源文件为struts-message.properties文件(与default.properties文件在同一目录下)
struts-messages.properties 文件里预定义 上传错误信息,通过覆盖对应key 显示中文信息
struts.messages.error.uploading=Error uploading: {0}
struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
{0}:<input type="file" name="uploadImage"> 中name属性的值
{1}:上传文件的真实名称
{2}:上传文件保存到临时目录的名称
{3}:上传文件的类型(对struts.messages.error.file.too.large是上传文件的大小)
关于这些可能出现的错误信息,在FileUploadInterceptor拦截器中的acceptFile方法中进行的拦截获取
这个方法的源代码如下所示:
protected boolean acceptFile(Object action, File file, String filename, String contentType, String inputName, ValidationAware validation) {
boolean fileIsAcceptable = false;
// If it's null the upload failed
if (file == null) {
String errMsg = getTextMessage(action, "struts.messages.error.uploading", new String[]{inputName});
if (validation != null) {
validation.addFieldError(inputName, errMsg);
}
if (LOG.isWarnEnabled()) {
LOG.warn(errMsg);
}
} else if (maximumSize != null && maximumSize < file.length()) {
String errMsg = getTextMessage(action, "struts.messages.error.file.too.large", new String[]{inputName, filename, file.getName(), "" + file.length()});
if (validation != null) {
validation.addFieldError(inputName, errMsg);
}
if (LOG.isWarnEnabled()) {
LOG.warn(errMsg);
}
} else if ((!allowedTypesSet.isEmpty()) && (!containsItem(allowedTypesSet, contentType))) {
String errMsg = getTextMessage(action, "struts.messages.error.content.type.not.allowed", new String[]{inputName, filename, file.getName(), contentType});
if (validation != null) {
validation.addFieldError(inputName, errMsg);
}
if (LOG.isWarnEnabled()) {
LOG.warn(errMsg);
}
} else if ((!allowedExtensionsSet.isEmpty()) && (!hasAllowedExtension(allowedExtensionsSet, filename))) {
String errMsg = getTextMessage(action, "struts.messages.error.file.extension.not.allowed", new String[]{inputName, filename, file.getName(), contentType});
if (validation != null) {
validation.addFieldError(inputName, errMsg);
}
if (LOG.isWarnEnabled()) {
LOG.warn(errMsg);
}
} else {
fileIsAcceptable = true;
}
return fileIsAcceptable;
}
但是发现默认的错误信息提示是英文的,要想展示中文的,实现国际化 步骤如下:
第一步:创建新的资源文件 例如fileuploadmessage.properties, 放在src下,在该资源文件中增加如下信息
struts.messages.error.uploading=上传错误: {0}
struts.messages.error.file.too.large=上传文件太大: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=上传文件的类型不允许: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=上传文件的后缀名不允许: {0} "{1}" "{2}" {3}
第二步:在struts.xml文件中加载该资源文件
struts.messages.error.file.too.large=上传文件太大: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=上传文件的类型不允许: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=上传文件的后缀名不允许: {0} "{1}" "{2}" {3}
第二步:在struts.xml文件中加载该资源文件
<!-- 配置上传文件的出错信息的资源文件 -->
<constant name="struts.custom.i18n.resources" value=“cn….xxx.fileuploadmessage“/>
4、关于多文件上传时的每个文件大小控制以及上传文件类型控制
<constant name="struts.custom.i18n.resources" value=“cn….xxx.fileuploadmessage“/>
4、关于多文件上传时的每个文件大小控制以及上传文件类型控制
对于多文件上传,在服务器端只需要将action属性声明为List集合或数组即可,在JSP页面上的每个上传组件的名称仍然是action中File数组或集合的属性值。例如:
private List<File> upload;
private List<String> uploadContentType;
private List<String> uploadFileName;
private List<String> uploadContentType;
private List<String> uploadFileName;
至于控制每一个上传文件的大小以及上传文件的类型,可以配置FileUploadInterceptor拦截器中的参数,通过其属性进行控制。FileUpload拦截器负责处理文件的上传操作,它是默认的defaultStack拦截器栈中的一员,它有三个属性可以设置
- maximumSize: 上传文件的最大长度(以字节为单位), 默认值为 2 MB
- allowedTypes: 允许上传文件的类型, 各类型之间以逗号分隔
- allowedExtensions: 允许上传文件扩展名, 各扩展名之间以逗号分隔
因为父包struts-default中即struts-default.xml中 文件上传拦截器的name名称被命名为fileUpload 所以在配置拦截器参数param时 为fileUpload.xxx属性 表示父包内 名称为fileUpload代表的文件上传拦截器的xxx属性 利用了ognl技术。
多文件上传的关键代码如下:
JSP:
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadImages">
<input type="file" name="uploadImages">
</form>
Action:
public class uploadAction{
private File[] uploadImages;//得到上传的文件
private String[] uploadImagesContentType;//得到文件的类型
private String[] uploadImagesFileName;//得到文件的名称
//这里略省了属性的getter/setter方法
public String saveFiles() throws Exception{
ServletContext sc = ServletActionContext.getServletContext();
String realpath = sc.getRealPath("/uploadfile");
try {
if(uploadImages!=null&&uploadImages.length>0){
for(int i=0;i<uploadImages.length;i++){
File destFile = new File(realpath,uploadImageFileNames[i]);
FileUtils.copyFile(uploadImages[i], destFile);
}
}
} catch (IOException e) {
e.printStackTrace();}return "success";
}
}
Struts2中的文件下载
平常文件下载概述:
1、超链接
2、服务器编码,通过流向客户端写回。
- 设置mimeType,通过response设置response.setContentType(String mimetype);
- 设置附件下载,通过response设置 response.setHeader("Content-disposition;filename=xxx");
- 通过response获取流,将要下载的信息写出
Struts2中文件下载:
通过<result type="stream">完成
在struts-default.xml中有result-type结果集视图,在结果集视图中有
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
StreamResult类中有三个属性:
protected String contentType = "text/plain"; //用于设置下载文件的mimeType类型
protected String contentDisposition = "inline";//用于设置进行下载操作以及下载文件的名称
protected InputStream inputStream; //用于读取要下载的文件。
protected String contentDisposition = "inline";//用于设置进行下载操作以及下载文件的名称
protected InputStream inputStream; //用于读取要下载的文件。
public InputStream getInputStream() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("d:/upload/" + filename);
return fis;
}
<result type="stream">
<param name="contentType">text/plain</param>
<param name="contentDisposition">attachment;filename=a.txt</param>
<param name="inputStream">${inputStream}</param> 会调用当前action中的getInputStream方法。
</result>
问题1:<a href="${pageContext.request.contextPath}/download?filename=捕获.png">捕获.png</a>下载报错
原因:超连接是get请求,并且下载的文件是中文名称,乱码。
问题2:下载捕获文件时,文件名称就是a.txt ,下载文件后缀名是png,而我们在配置文件中规定就是txt?
<result type="stream">
<param name="contentType">${contentType}</param> <!-- 调用当前action中的getContentType()方法 -->
<param name="contentDisposition">attachment;filename=${downloadFileName}</param>
<param name="inputStream">${inputStream}</param><!-- 调用当前action中的getInputStream()方法 -->
</result>
在struts2中进行下载时,如果使用<result type="stream">它有缺陷,例如:下载点击后,取消下载,服务器端会产生异常。在开发中,解决方案:可以下载一个struts2下载操作的插件,它解决了stream问题。
FileInputStream fis = new FileInputStream("d:/upload/" + filename);
return fis;
}
<result type="stream">
<param name="contentType">text/plain</param>
<param name="contentDisposition">attachment;filename=a.txt</param>
<param name="inputStream">${inputStream}</param> 会调用当前action中的getInputStream方法。
</result>
问题1:<a href="${pageContext.request.contextPath}/download?filename=捕获.png">捕获.png</a>下载报错
原因:超连接是get请求,并且下载的文件是中文名称,乱码。
问题2:下载捕获文件时,文件名称就是a.txt ,下载文件后缀名是png,而我们在配置文件中规定就是txt?
<result type="stream">
<param name="contentType">${contentType}</param> <!-- 调用当前action中的getContentType()方法 -->
<param name="contentDisposition">attachment;filename=${downloadFileName}</param>
<param name="inputStream">${inputStream}</param><!-- 调用当前action中的getInputStream()方法 -->
</result>
在struts2中进行下载时,如果使用<result type="stream">它有缺陷,例如:下载点击后,取消下载,服务器端会产生异常。在开发中,解决方案:可以下载一个struts2下载操作的插件,它解决了stream问题。
关键代码(简单示例)如下:
文件上传与下载程序示例
项目结构如下:
UploadAction
package cn.itcast.action;
import java.io.File;
import java.util.List;
import org.apache.commons.io.FileUtils;
import com.opensymphony.xwork2.ActionSupport;
public class UploadAction extends ActionSupport {
// 在action类中需要声明三个属性
private List<File> upload;
private List<String> uploadContentType;
private List<String> uploadFileName;
public List<File> getUpload() {
return upload;
}
public void setUpload(List<File> upload) {
this.upload = upload;
}
public List<String> getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(List<String> uploadContentType) {
this.uploadContentType = uploadContentType;
}
public List<String> getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(List<String> uploadFileName) {
this.uploadFileName = uploadFileName;
}
@Override
public String execute() throws Exception {
for (int i = 0; i < upload.size(); i++) {
System.out.println("上传文件的类型:" + uploadContentType.get(i));
System.out.println("上传文件的名称:" + uploadFileName.get(i));
FileUtils.copyFile(upload.get(i), new File("d:/upload",
uploadFileName.get(i)));
}
return null;
}
}
DownloadAction
package cn.itcast.action;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.apache.struts2.ServletActionContext;
import cn.itcast.utils.DownloadUtils;
import com.opensymphony.xwork2.ActionSupport;
public class DownloadAction extends ActionSupport {
private String filename; // 要下载文件的名称
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
// 设置下载文件mimeType类型
public String getContentType() {
String mimeType = ServletActionContext.getServletContext().getMimeType(
filename);
return mimeType;
}
// 获取下载文件名称
public String getDownloadFileName() throws UnsupportedEncodingException {
return DownloadUtils.getDownloadFileName(ServletActionContext
.getRequest().getHeader("user-agent"), filename);
}
public InputStream getInputStream() throws UnsupportedEncodingException,
FileNotFoundException {
filename = new String(filename.getBytes("iso8859-1"), "utf-8"); // 解决中文名乱码
FileInputStream fis = new FileInputStream("d:/upload/" + filename);
return fis;
}
@Override
public String execute() throws Exception {
System.out.println("进行下载......");
return super.execute();
}
}
配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.custom.i18n.resources" value="message"></constant>
<package name="default" namespace="/" extends="struts-default">
<action name="upload" class="cn.itcast.action.UploadAction">
<result name="input">/upload.jsp</result>
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">2097152</param>
<param name="fileUpload.allowedExtensions">txt,mp3,docx</param>
</interceptor-ref>
</action>
<action name="download" class="cn.itcast.action.DownloadAction">
<result type="stream">
<param name="contentType">${contentType}</param> <!-- 调用当前action中的getContentType()方法 -->
<param name="contentDisposition">attachment;filename=${downloadFileName}</param><!--
会调用Action中getFileName方法 -->
<param name="inputStream">${inputStream}</param> <!-- 调用当前action中的getInputStream()方法 -->
</result>
</action>
</package>
</struts>
message.properties
upload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<s:fielderror />
<form action="${pageContext.request.contextPath}/upload" method="POST"
enctype="multipart/form-data">
<input type="file" name="upload"><br>
<input type="file" name="upload"><br>
<input type="file" name="upload"><br>
<input type="submit" value="上传">
</form>
</body>
</html>
download.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/download?filename=a.txt">a.txt</a><br>
<a href="${pageContext.request.contextPath}/download?filename=测试下载.docx">测试下载.docx</a>
</body>
</html>