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>
结果就是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 提供了一个空白的实现
Struts2自带的拦截器
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()方法就可以。如下图:
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文件中声明。
步骤三:.在action中指定使用哪些拦截器. <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>
因为Struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用Struts2框架提供的众多功能。
如果希望包下的所有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>
而且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中一个方法。
要求:对于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;
}
}
新建登录的Action类
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的代理对象。
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帮助我们完成文件上传操作。
<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>
第三步:在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";
}
}
最后定义上传出错要转向的页面
第四步:在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>
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"/>
2、在Struts2中默认使用的是Commons-fileupload进行文件上传
# struts.multipart.parser=cos # 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文件中加载该资源文件
<!-- 配置上传文件的出错信息的资源文件 --> <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;
至于控制每一个上传文件的大小以及上传文件的类型,可以配置FileUploadInterceptor拦截器中的参数,通过其属性进行控制。FileUpload拦截器负责处理文件的上传操作,它是默认的defaultStack拦截器栈中的一员,它有三个属性可以设置
maximumSize: 上传文件的最大长度(以字节为单位), 默认值为 2 MBallowedTypes: 允许上传文件的类型, 各类型之间以逗号分隔allowedExtensions: 允许上传文件扩展名, 各扩展名之间以逗号分隔
可以在 struts.xml 文件中覆盖这 3 个属性,如下图所示:
因为父包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; //用于读取要下载的文件。
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问题。
关键代码(简单示例)如下:
文件上传与下载程序示例
项目结构如下:
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>
转载请注明原文地址: https://ju.6miu.com/read-18674.html