Struts2 运行流程分析
- 请求发送给 StrutsPrepareAndExecuteFilter
- StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 Struts2 请求(即是否返回一个非空的 ActionMapping 对象)
- 若 ActionMapper 认为该请求是一个 Struts2 请求,则 StrutsPrepareAndExecuteFilter 把请求的处理交给 ActionProxy
- ActionProxy 通过 Configuration Manager 询问框架的配置文件,确定需要调用的 Action 类及 Action 方法
- ActionProxy 创建一个 ActionInvocation 的实例,并进行初始化
- ActionInvocation 实例在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
- Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置找到对应的返回结果。调用结果的 execute 方法,渲染结果。在渲染的过程中可以使用Struts2 框架中的标签。
- 执行各个拦截器 invocation.invoke() 之后的代码
- 把结果发送到客户端
相关API
ActionMapping:一个简单的类,它保存用于调用Struts操作的操作映射信息。名称和命名空间是必需的
ActionMapper:当给定HttpServletRequest时,如果没有匹配的操作调用请求,ActionMapper可能返回null,或者它可能返回一个ActionMapping,该ActionMapping描述了框架要尝试的操作调用
ActionProxy:ActionProxy是XWork和action之间的一个额外层,因此可以使用不同的代理。
ActionInvocation:ActionInvocation表示操作的执行状态。它保存拦截器和操作实例。通过反复重新进入invoke()方法的执行,最初由ActionProxy执行,然后由拦截器执行,拦截器全部执行,然后执行操作和结果
输入验证
概述
验证分为两种:
> 声明式验证
>> 对哪个 Action 或 Model 的那个字段进行验证
>> 使用什么验证规则
>> 如果验证失败, 转向哪一个页面, 显示是什么错误消息
> 编程式验证
声明式验证的 helloworld
I. 先明确对哪一个 Action 的哪一个字段进行验证: age
II. 编写配置文件:
> 把 struts-2.3.15.3\apps\struts2-blank\WEB-INF\classes\example 下的 Login-validation.xml 文件复制到
当前 Action 所在的包下.
> 把该配置文件改为: 把 Login 改为当前 Action 的名字.
> 编写验证规则: 参见 struts-2.3.15.3/docs/WW/docs/validation.html 文档即可.
> 在配置文件中可以定义错误消息:
<field name="age">
<field-validator type="int">
<param name="min">20</param>
<param name="max">50</param>
<message>^^Age needs to be between ${min} and ${max}</message>
</field-validator>
</field>
该错误消息可以国际化吗.。可以
<message key="error.int"></message>.
再在 国际化资源文件 中加入一个键值对: error.int=^^^Age needs to be between ${min} and ${max}
III. 若验证失败, 则转向 input 的那个 result. 所以需要配置 name=input 的 result
/validation.jsp
IV. 如何显示错误消息呢 ?
若使用的是非 simple, 则自动显示错误消息.
若使用的是 simple 主题, 则需要 s:fielderror 标签或直接使用 EL 表达式(使用 OGNL)
${fieldErrors.age[0] }
OR
<s:fielderror fieldName=“age”></s:fielderror>*
3). 注意: 若一个 Action 类可以应答多个 action 请求, 多个 action 请求使用不同的验证规则, 怎么办 ?
> 为每一个不同的 action 请求定义其对应的验证文件: ActionClassName-AliasName-validation.xml
> 不带别名的配置文件: ActionClassName-validation.xml 中的验证规则依然会发生作用. 可以把各个 action 公有的验证规则
配置在其中. 但需要注意的是, 只适用于某一个 action 的请求的验证规则就不要这里再配置了.
例子:
//i18n.jsp
<%@page import="java.util.Date"%>
<%@ 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>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:form action="testI18n.action">
<s:textfield name="age" label="age"></s:textfield>
<s:submit name="submit"></s:submit>
</s:form>
</body>
</html>
//
package product;
import java.util.Arrays;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
public class TestI18nAction extends ActionSupport {
/**
*
*/
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String execute() throws Exception {
System.out.println(age);
return SUCCESS;
}
}
///
//TestI18nAction.java同包下的TestI18nAction-validation.xml
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<!-- 针对 age 属性进行验证. 基于字段的验证 -->
<field name="age">
<field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message key="age must between ${min}and ${max}"></message>
</field-validator>
</field>
</validators>
//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="i18n"></constant>
<package name="hello" extends="struts-default" >
<action name="testI18n" class="product.TestI18nAction">
<result name="input">/i18n.jsp</result>
<result>/success.jsp</result>
</action>
</package>
</struts>
国际化提示
TestI18nAction-validation.xml改进
<message key="error.int"></message>
声明式验证程序可以分为两类:
字段验证: 判断某个字段属性的输入是否有效
非字段验证: 不只针对某个字段,而是针对多个字段的输入值之间的逻辑关系进行校验。例如:对再次输入密码的判断。
使用一个声明式验证程序需要 3 个步骤:
- 确定哪些 Action 字段需要验证
- 编写一个验证程序配置文件. 它的文件名必须是以下两种格式之一:
若一个 Action 类的多个 action 使用同样的验证规则: ActionClass-validation.xml
若一个 Action 类的多个 action 使用不同的验证规则: ActionClass-alias-validation.xml, 例如 UserAction-User_create-validation.xml,不带别名的配置文件: ActionClassName-validation.xml 中的验证规则依然会发生作用. 可以把各个 action 公有的验证规则.
配置在其中. 但需要注意的是, 只适用于某一个 action 的请求的验证规则就不要这里再配置了 - 确定验证失败时的响应页面: 在 struts.xml 文件中定义一个 的元素.
Struts2 内建的验证
conversion validator:转换验证器
date validator:日期验证器
double validator:浮点验证器
email validator:email 验证器
expression validator:表达式验证器
fieldexpression validator:字段表达式验证器
int validator:整型验证器
regex validator:正则表达式验证器
required validator:非空验证器
requiredstring validator:非空字符串验证器
stringlength validator:字符串长度验证器
url validator:url 格式验证器
visitor validator:复合属性验证器
验证程序的配置
Struts2 内建的验证程序
date: 确保某给定日期字段的值落在一个给定的范围内
max:相关字段的最大值. 若没给出这个参数, 该字段将没有最大值限制
min:相关字段的最小值. 若没给出这个参数, 该字段将没有最小值限制
email: 检查给定 String 值是否是一个合法的 email
url: 检查给定 String 值是否是一个合法的 url
regex: 检查某给定字段的值是否与一个给定的正则表达式模式相匹配.
expresssion*: 用来匹配的正则表达式
caseSensitive: 是否区分字母的大小写. 默认为 true
trim: 是否去除前后空格. 默认为 true
expression 和 fieldexpression: 用来验证给定字段是否满足一个 OGNL 表达式.
前者是一个非字段验证程序, 后者是一个字段验证程序.
前者在验证失败时将生成一个 action 错误, 而后者在验证失败时会生成一个字段错误
expression*: 用来进行验证的 OGNL 表达式
conversion: 检查对给定 Action 属性进行的类型转换是否会导致一个转换错误. 该验证程序还可以在默认的类型转换消息的基础上添加一条自定义的消息
声明式验证框架的原理
Struts2 默认的拦截器栈中提供了一个 validation 拦截器
每个具体的验证规则都会对应具体的一个验证器. 有一个配置文件把验证规则名称和验证器关联起来了. 而实际上验证的是那个验证器.
该文件位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
短路验证器
<validator …/> 元素和 <field-validator …/> 元素可以指定一个可选的 short-circuit 属性,该属性指定该验证器是否是短验证器**,默认值为 false。**
对同一个字段内的多个验证器,如果一个短路验证器验证失败,其他验证器不会继续校验
例子:
//TestI18nAction-validation.xml field内加入以下首行
<field-validator type="conversion" short-circuit="true">
<message>^Conversion Error Occurred</message>
</field-validator>
若类型转换失败, 默认情况下还会执行后面的拦截器, 还会进行 验证. 可以通过修改 ConversionErrorInterceptor 源代码的方式使
当类型转换失败时, 不再执行后续的验证拦截器, 而直接返回 input 的 result
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(va.hasFieldErrors() || va.hasActionErrors()){
return "input";
}
}
非字段验证示例
关于非字段验证: 不是针对于某一个字段的验证.
显示非字段验证的错误消息, 使用 s:actionerror 标签: <s:actionerror/>
//TestI18nAction-validation.xml </validators>前一行加入
<validator type="expression">
<param name="expression"><![CDATA[password==password2]]></param>
<message>Password is not equals to password2</message>
</validator>
///
//在i18n.jsp form中加入
<s:password name="password" label="password"></s:password>
<s:password name="password2" label="password2"></s:password>
<s:actionerror/>
///
//在action中加入相应的password 和2字段并有set get
字段非字段验证区别
字段验证字段优先,可以为一个字段配置多个验证规则
非字段验证验证规则优先
大部分验证规则支持两种验证器,但个别的验证规则只能使用非字段验证,例如表达式验证。
错误消息的重用性
多个字段使用同样的验证规则,可使用同一条验证消息
例子:
在前面的基础上
//i18n.jsp
<s:textfield name="count" label="count"></s:textfield>
//
//TestI18nAction加入相应的 int count字段和getset方法
//
//TestI18nAction-validation.xml </validators>前面加入
<field name="count">
<field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message key="error"></message>
</field-validator>
</field>
//
i18n.properties加入以下
自定义验证器
I. 定义一个验证器的类
> 自定义的验证器都需要实现 Validator.
> 可以选择继承 ValidatorSupport 或 FieldValidatorSupport 类
> 若希望实现一个一般的验证器, 则可以继承 ValidatorSupport
> 若希望实现一个字段验证器, 则可以继承 FieldValidatorSupport
> 具体实现可以参考目前已经有的验证器.
> 若验证程序需要接受一个输入参数, 需要为这个参数增加一个相应的属性
例子:
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;
public class IDCardValidator extends FieldValidatorSupport {
@Override
public void validate(Object object) throws ValidationException {
//1. 获取字段的名字和值
String fieldName = getFieldName();
Object value = this.getFieldValue(fieldName, object);
//2. 验证 IDCard 只是进行判断的类
IDCard idCard = new IDCard();
boolean result = idCard.Verify((String)value);
//3. 若验证失败, 则 ...
if(!result){
addFieldError(fieldName, object);
}
}
}
II. 在配置文件中配置验证器
> 默认情况下下, Struts2 会在 类路径的根目录下加载 validators.xml 文件. 在该文件中加载验证器.
该文件的定义方式同默认的验证器的那个配置文件: 位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
> 若类路径下没有指定的验证器, 则从 com.opensymphony.xwork2.validator.validators 下的 default.xml 中的验证器加载
例子:
src下的validators.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Definition 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<!-- START SNIPPET: validators-default -->
<validators>
<validator name="idcard" class="product.IDCardValidator"/>
</validators>
III. 使用: 和目前的验证器一样.
加入:
i18n.jsp
<s:textfield name="idcard" label="idcard"></s:textfield>
在TestI18nAction.java加入string相应的字段getset
在TestI18nAction-validation.xml文件中声明使用
<field name="idCard">
<field-validator type="idcard">
<message>It is not a idCard!</message>
</field-validator>
</field>
编程验证
Struts2 提供了一个 Validateable 接口, 可以使动作类实现这个接口以提供编程验证功能.
ActionSupport 类已经实现了 Validateable 接口
文件的上传
上传概述
要想使用 HTML 表单上传一个或多个文件
须把 HTML 表单的 enctype 属性设置为 multipart/form-data
须把 HTML 表单的method 属性设置为 post
需添加 字段.
1). 表单需要注意3 点
2). Struts2 的文件上传实际上使用的是 Commons FileUpload 组件, 所以需要导入
commons-fileupload-1.3.jar
commons-io-2.0.1.jar
3). Struts2 进行文件上传需要使用 FileUpload 拦截器
4). 基本的文件的上传: 直接在 Action 中定义如下 3 个属性, 并提供对应的 getter 和 setter
//文件对应的 File 对象
private File [fileFieldName];
//文件类型
private String [fileFieldName]ContentType;
//文件名
private String [fileFieldName]FileName;
5). 使用 IO 流进行文件的上传即可.
例子:
//i18n.jsp
<%@page import="java.util.Date"%>
<%@ 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>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:debug></s:debug>
<s:form action="uploadfile.action" method="post" enctype="multipart/form-data">
<s:textfield name="pptd" label="pptd"></s:textfield>
<s:file name="ppt" label="ppt"></s:file>
<s:submit name="submit"></s:submit>
</s:form>
</body>
</html>
///
//对应的Action
package product;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UploadFile extends ActionSupport{
private File ppt;
private String pptFileName;
private String pptContentType;
private String pptd;
public String getPptd() {
return pptd;
}
public void setPptd(String pptd) {
this.pptd = pptd;
}
public File getPpt() {
return ppt;
}
public void setPpt(File ppt) {
this.ppt = ppt;
}
public String getPptFileName() {
return pptFileName;
}
public void setPptFileName(String pptFileName) {
this.pptFileName = pptFileName;
}
public String getPptContentType() {
return pptContentType;
}
public void setPptContentType(String pptContentType) {
this.pptContentType = pptContentType;
}
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println(ppt);
System.out.println(pptContentType);
System.out.println(pptFileName);
System.out.println(pptd);
ServletContext servletContext = ServletActionContext.getServletContext();
String dir = servletContext.getRealPath("/files/" + pptFileName);
System.out.println(dir);
FileOutputStream out = new FileOutputStream(dir);
FileInputStream in = new FileInputStream(ppt);
byte [] buffer = new byte[1024];
int len = 0;
while((len = in.read(buffer)) != -1){
out.write(buffer, 0, len);
}
out.close();
in.close();
return "success";
}
}
//
//Struts.xml做相应的配置
6)若传递多个文件, 则上述的 3 个属性, 可以改为 List 类型! 多个文件域的 name 属性值需要一致. 当同一属性回显会出问题解决如下
PPTFile: <s:file name="ppt" label="PPTFile"></s:file>
PPTDesc: <s:textfield name="pptDesc[0]" label="PPTDesc"></s:textfield>
<br><br>
PPTFile:<s:file name="ppt" label="PPTFile"></s:file>
PPTDesc:<s:textfield name="pptDesc[1]" label="PPTDesc"></s:textfield>
<br><br>
PPTFile:<s:file name="ppt" label="PPTFile"></s:file>
PPTDesc:<s:textfield name="pptDesc[2]" label="PPTDesc"></s:textfield>
7)可以通过配置 FileUploadInterceptor 拦截器的参数的方式来进行限制
maximumSize (optional) - 默认的最大值为 2M. 上传的单个文件的最大值
allowedTypes (optional) - 允许的上传文件的类型. 多个使用 , 分割
allowedExtensions (optional) - 允许的上传文件的扩展名. 多个使用 , 分割.
注意: 在 org.apache.struts2 下的 default.properties 中有对上传的文件总的大小的限制. 可以使用常量的方式来修改该限制
struts.multipart.maxSize=2097152
8)定制错误消息. 可以在国际化资源文件中定义如下的消息:
struts.messages.error.uploading - 文件上传出错的消息
struts.messages.error.file.too.large - 文件超过最大值的消息
struts.messages.error.content.type.not.allowed - 文件内容类型不合法的消息
struts.messages.error.file.extension.not.allowed - 文件扩展名不合法的消息
问题: 此种方式定制的消息并不完善. 可以参考 org.apache.struts2 下的 struts-messages.properties, 可以提供更多的定制信息.
例子:
///struts.xml
<interceptors>
<interceptor-stack name="atguigustack">
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">2097152</param>
<!--
<param name="fileUpload.allowedTypes">text/html,text/xml</param>
<param name="fileUpload.allowedExtensions">html,dtd,xml</param>
-->
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="atguigustack"></default-interceptor-ref>
问题: 此种方式定制的消息并不完善. 可以参考 org.apache.struts2 下的 struts-messages.properties, 可以提供更多的定制信息.
文件下载
概述
在某些应用程序里, 可能需要动态地把一个文件发送到用户的浏览器中, 而这个文件的名字和存放位置在编程时是无法预知的
1). Struts2 中使用 type=“stream” 的 result 进行下载即可
2). 具体使用细节参看 struts-2.3.15.3-all/struts-2.3.15.3/docs/WW/docs/stream-result.html
3). 可以为 stream 的 result 设定如下参数
contentType: 结果类型
contentLength: 下载的文件的长度
contentDisposition: 设定 Content-Dispositoin 响应头. 该响应头指定接应是一个文件下载类型, 一般取值为 attachment;filename=“document.pdf”.
inputName: 指定文件输入流的 getter 定义的那个属性的名字. 默认为 inputStream
bufferSize: 缓存的大小. 默认为 1024
allowCaching: 是否允许使用缓存
contentCharSet: 指定下载的字符集
4). 以上参数可以在 Action 中以 getter 方法的方式提供!
例子:
//download.jsp
<a href="testDownload">Down Load</a>
*//
//action
package product;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class DownLoadAction extends ActionSupport {
/**
*
*/
private static final long serialVersionUID = 1L;
private String contentType;
private long contentLength;
private String contentDisposition;
private InputStream inputStream;
public String getContentType() {
return contentType;
}
public long getContentLength() {
return contentLength;
}
public String getContentDisposition() {
return contentDisposition;
}
public InputStream getInputStream() {
return inputStream;
}
@Override
public String execute() throws Exception {
contentType = "text/html";
contentDisposition = "attachment;filename=index.html";
ServletContext servletContext =
ServletActionContext.getServletContext();
String fileName = servletContext.getRealPath("/files/index.html");
inputStream = new FileInputStream(fileName);
contentLength = inputStream.available();
return SUCCESS;
}
}
//
//struts.xml
<action name="testDownload" class="product.DownLoadAction">
<result type="stream">
<param name="bufferSize">2048</param>
</result>
</action>
防止表单重复提交
概述
表单的重复提交:
若刷新表单页面, 再提交表单不算重复提交.
在不刷新表单页面的前提下:
多次点击提交按钮
已经提交成功, 按 “回退” 之后, 再点击 “提交按钮”.
在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)“
重复提交的缺点:
加重了服务器的负担
可能导致错误操作.
注意:
>> 若刷新表单页面, 再提交表单不算重复提交
>> 若使用的是 redirect 的响应类型, 已经提交成功后, 再点击 “刷新”, 不是表单的重复提交
Struts2 解决表单的重复提交问题:
I. 在 s:form 中添加 s:token 子标签
> 生成一个隐藏域
> 在 session 添加一个属性值
> 隐藏域的值和 session 的属性值是一致的.
II. 使用 Token 或 TokenSession 拦截器.
> 这两个拦截器均不在默认的拦截器栈中, 所以需要手工配置一下
> 若使用 Token 拦截器, 则需要配置一个 token.valid 的 result
> 若使用 TokenSession 拦截器, 则不需要配置任何其它的 result
III. Token VS TokenSession
> 都是解决表单重复提交问题的
> 使用 token 拦截器会转到 token.valid 这个 result
> 使用 tokenSession 拦截器则还会响应那个目标页面, 但不会执行 tokenSession 的后续拦截器. 就像什么都没发生过一样!
IV. 可以使用 s:actionerror 标签来显示重复提交的错误消息.
该错误消息可以在国际化资源文件中覆盖. 该消息可以在 struts-messages.properties 文件中找到
struts.messages.invalid.token=^^The form has already been processed or no token was supplied, please try again.
例子:
//index.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>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:form action="testToken">
<s:token></s:token>
<s:textfield name="username" label="Username"></s:textfield>
<s:submit></s:submit>
</s:form>
</body>
</html>
//
//action
package product;
import com.opensymphony.xwork2.ActionSupport;
public class TokenAction extends ActionSupport{
/**
*
*/
private static final long serialVersionUID = 1L;
private String username;
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
@Override
public String execute() throws Exception {
Thread.sleep(2000);
System.out.println(username);
return SUCCESS;
}
}
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="i18n"></constant>
<package name="hello" extends="struts-default" >
<action name="testToken" class="product.TokenAction">
<interceptor-ref name="token"></interceptor-ref>
<result name="success">/success.jsp</result>
<result name="invalid.token">/error.jsp</result>
</action>
</package>
</struts>
错误性消息覆盖
自定义拦截器
Struts2 拦截器
拦截器(Interceptor)是 Struts 2 的核心组成部分。
Struts2 很多功能都是构建在拦截器基础之上的,例如文件的上传和下载、国际化、数据类型转换和数据校验等等。
Struts2 拦截器在访问某个 Action 方法之前或之后实施拦截
Struts2 拦截器是可插拔的, 拦截器是 AOP(面向切面编程) 的一种实现.
拦截器栈(Interceptor Stack): 将拦截器按一定的顺序联结成一条链. 在访问被拦截的方法时, Struts2 拦截器链中的拦截器就会按其之前定义的顺序被依次调用
示意图:
Struts2 自带的拦截器
Interceptor 接口
每个拦截器都是实现了 接口的 Java 类:
其中:
init: 该方法将在拦截器被创建后立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化
interecept: 每拦截一个请求, 该方法就会被调用一次.
destroy: 该方法将在拦截器被销毁之前被调用, 它在拦截器的生命周期内也只被调用一次.
Struts 会依次调用为某个 Action 而注册的每一个拦截器的 interecept 方法.
每次调用 interecept 方法时, Struts 会传递一个 ActionInvocation 接口的实例.
ActionInvocation: 代表一个给定 Action 的执行状态, 拦截器可以从该类的对象里获得与该 Action 相关联的 Action 对象和 Result 对象. 在完成拦截器自己的任务之后, 拦截器将调用 ActionInvocation 对象的 invoke 方法前进到 Action 处理流程的下一个环节.
AbstractInterceptor 类实现了 Interceptor 接口. 并为 init, destroy 提供了一个空白的实现
步骤
I. 定义一个拦截器的类
> 可以实现 Interceptor 接口
> 继承 AbstractInterceptor 抽象类
package product;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class my extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation arg0) throws Exception {
// TODO Auto-generated method stub
System.out.println("before");
String r=arg0.invoke();
System.out.println("after");
return r;
}
}
II. 在 struts.xml 文件配置.
<interceptors>
<interceptor name="hello1" class="product.my"></interceptor>
</interceptors>
<action name="testToken" class="product.TokenAction">
<interceptor-ref name="hello1"></interceptor-ref>
<result>/success.jsp</result>
</action>
III. 注意: 在自定义的拦截器中可以选择不调用 ActionInvocation 的 invoke() 方法. 那么后续的拦截器和 Action 方法将不会被调用.
Struts 会渲染自定义拦截器 intercept 方法返回值对应的 result
<interceptors>
<interceptor name="hello" class="com.MyInterceptor"></interceptor>
<interceptor-stack name="at">
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">2097152</param>
<!--
<param name="fileUpload.allowedTypes">text/html,text/xml</param>
<param name="fileUpload.allowedExtensions">html,dtd,xml</param>
-->
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="at"></default-interceptor-ref>