拦截器体系是struts2框架的重要组成部分,我们可以把struts2理解成一个空容器,而大量的拦截器完成了该框架的大部分操作。例如:解析请求参数,类型转换,将请求参数封装成DTO(Data Transfer Object),执行输入校验,解析文件上传表单的文件域,防止表单的多次提交等。
例如:<result name="input">/WEB-INF/jsp/remind.jsp</result>
struts2默认在execute方法执行之前,会先执行validate验证方法,如果发现有errors,直接就中转到input所指向的页面.跳转到input之前就根本没有执行action的方法.
ActionFormBean的validate方法验证不通过时,返回到input所指向页面的同时用ActionFormBean现有字段的值填充页面表单.
一、Struts2的内建拦截器
struts2内建了大量的拦截器,这些拦截器在struts-defaul.xml中已经详细的配置过了。只要你的struts.xml的包集成了struts-defaul包,那么就可以直接使用这些拦截器而不需要手动控制。
alias
(别名拦截器):允许参数在跨越多个请求时使用不同别名,该拦截器可将多个Action采用不同名字链接起来,然后用于处理同一信息。
autowiring (自动装配拦截器):主要用于当Struts2和Spring整合时,Struts2可以使用自动装配的方式来访问Spring容器中的Bean。
chain (链拦截器):构建一个Action链,使当前Action可以访问前一个Action的属性,一般和<result type="chain" .../>一起使用。
checkbox (多选框拦截器):将没有选中的checkbox项设置为false,协助管理多选框。在HTTP请求里,那些没有被选中的项通常没有任何值。
conversionError (转换器错误拦截器):这是一个负责处理类型转换错误的拦截器,它负责将类型转换错误从 ActionContext 中取出,并转换成Action的FieldError错误。
createSession (创建Session拦截器):该拦截器负责创建一个HttpSession对象,主要用于那些需要有HttpSession对象才能正常工作的拦截器中。
clearSession (清除Session拦截器):负责销毁HttpSession对象.
debugging (调试拦截器):当使用Struts2的开发模式时,这个拦截器会提供更多的调试信息。
execAndWait (执行和等待拦截器):后台执行Action时,给用户显示一个过渡性的等待页面。
externalRef (扩展拦截器):负责扩展引用
exception (异常拦截器):将Action抛出的异常映射到结果,这样就通过重定向自动处理异常。
fileUpload (文件上传拦截器):这个拦截器主要用于文件上传,它负责解析表单中文件域的内容。
i18n (国际化拦截器):主要负责把用户所选的语言、区域放入用户Session中。
logger (日志拦截器):主要是输出Action的名字,提供简单的日志输出。
modelDriven (模型驱动拦截器):这是一个用于模型驱动的拦截器,当某个Action类实现了ModelDriven接口时,它负责把 getModel ()方法的结果堆入ValueStack中。
scopedModelDriven (作用域模型驱动拦截器):如果一个Action实现了一个ScopedModelDriven接口,该拦截器负责从指定生存范围中找出指定的Model,并将通过setModel方法将该Model传给Action实例。
params (参数过滤拦截器):这是一个最基本的拦截器,它负责解析HTTP请求中的参数,并将参数值设置成Action对应的属性值。
prepare (预备拦截器):如果action实现了Preparable接口,将会调用该拦截器的prepare()方法。
staticParams (静态参数拦截器):这个拦截器负责将xml中<action>标签下<param>标签中的参数传入action。
scope (作用域拦截器):这是范围转换拦截器,它可以将Action状态信息保存到HttpSession范围,或者保存到 ServletContext 范围内。
servletConfig (Servlet配置拦截器):如果某个Action需要直接访问Servlet API,就是通过这个拦截器实现的,它提供访问HttpServletRequest和HttpServletResponse的方法,以map方式访问。
roles (角色拦截器):这是一个JAAS(Java Authentication and Authorization Service,Java授权和认证服务)拦截器,只有当浏览者取得合适的授权后,才可以调用被该拦截器拦截的Action。
timer (计时拦截器):这个拦截器负责输出Action的执行时间,在分析该Action的性能瓶颈时比较有用。
token (令牌拦截器):这个拦截器主要用于阻止重复提交,它检查传到Action中的token,从而防止多次提交。
tokenSession (令牌会话拦截器):这个拦截器的作用与前一个基本类似,只是它把非法提交的数据保存在HttpSession中,不跳转到错误页面,再次生成与第一次相同的响应页面
validation (验证拦截器):通过执行在xxxAction-validation.xml中定义的校验器,从而完成数据校验。
workflow (工作流拦截器):这个拦截器负责调用Action类中的validate方法,如果校验失败,则不执行业务方法,而是返回input的逻辑视图。
jsonValidation (json拦截器):验证失败时,可以将fieldError和actionErrors信息序列化成json,返回给客户端
profiling (概要拦截器):允许Action记录简单的概要信息。
actionMappingParams (Action映射拦截器):Parameters set by the action mapping are not set/not available through ParameterAware
annotationWorkflow (注解工作流拦截器):利用注解替代XML配置,使用annotationWorkflow拦截器可以使用@After、@Before、@BeforeResult等注解,执行流程为before-execute-beforeResult-after顺序
autowiring (自动装配拦截器):主要用于当Struts2和Spring整合时,Struts2可以使用自动装配的方式来访问Spring容器中的Bean。
chain (链拦截器):构建一个Action链,使当前Action可以访问前一个Action的属性,一般和<result type="chain" .../>一起使用。
checkbox (多选框拦截器):将没有选中的checkbox项设置为false,协助管理多选框。在HTTP请求里,那些没有被选中的项通常没有任何值。
conversionError (转换器错误拦截器):这是一个负责处理类型转换错误的拦截器,它负责将类型转换错误从 ActionContext 中取出,并转换成Action的FieldError错误。
createSession (创建Session拦截器):该拦截器负责创建一个HttpSession对象,主要用于那些需要有HttpSession对象才能正常工作的拦截器中。
clearSession (清除Session拦截器):负责销毁HttpSession对象.
debugging (调试拦截器):当使用Struts2的开发模式时,这个拦截器会提供更多的调试信息。
execAndWait (执行和等待拦截器):后台执行Action时,给用户显示一个过渡性的等待页面。
externalRef (扩展拦截器):负责扩展引用
exception (异常拦截器):将Action抛出的异常映射到结果,这样就通过重定向自动处理异常。
fileUpload (文件上传拦截器):这个拦截器主要用于文件上传,它负责解析表单中文件域的内容。
i18n (国际化拦截器):主要负责把用户所选的语言、区域放入用户Session中。
logger (日志拦截器):主要是输出Action的名字,提供简单的日志输出。
modelDriven (模型驱动拦截器):这是一个用于模型驱动的拦截器,当某个Action类实现了ModelDriven接口时,它负责把 getModel ()方法的结果堆入ValueStack中。
scopedModelDriven (作用域模型驱动拦截器):如果一个Action实现了一个ScopedModelDriven接口,该拦截器负责从指定生存范围中找出指定的Model,并将通过setModel方法将该Model传给Action实例。
params (参数过滤拦截器):这是一个最基本的拦截器,它负责解析HTTP请求中的参数,并将参数值设置成Action对应的属性值。
prepare (预备拦截器):如果action实现了Preparable接口,将会调用该拦截器的prepare()方法。
staticParams (静态参数拦截器):这个拦截器负责将xml中<action>标签下<param>标签中的参数传入action。
scope (作用域拦截器):这是范围转换拦截器,它可以将Action状态信息保存到HttpSession范围,或者保存到 ServletContext 范围内。
servletConfig (Servlet配置拦截器):如果某个Action需要直接访问Servlet API,就是通过这个拦截器实现的,它提供访问HttpServletRequest和HttpServletResponse的方法,以map方式访问。
roles (角色拦截器):这是一个JAAS(Java Authentication and Authorization Service,Java授权和认证服务)拦截器,只有当浏览者取得合适的授权后,才可以调用被该拦截器拦截的Action。
timer (计时拦截器):这个拦截器负责输出Action的执行时间,在分析该Action的性能瓶颈时比较有用。
token (令牌拦截器):这个拦截器主要用于阻止重复提交,它检查传到Action中的token,从而防止多次提交。
tokenSession (令牌会话拦截器):这个拦截器的作用与前一个基本类似,只是它把非法提交的数据保存在HttpSession中,不跳转到错误页面,再次生成与第一次相同的响应页面
validation (验证拦截器):通过执行在xxxAction-validation.xml中定义的校验器,从而完成数据校验。
workflow (工作流拦截器):这个拦截器负责调用Action类中的validate方法,如果校验失败,则不执行业务方法,而是返回input的逻辑视图。
jsonValidation (json拦截器):验证失败时,可以将fieldError和actionErrors信息序列化成json,返回给客户端
profiling (概要拦截器):允许Action记录简单的概要信息。
actionMappingParams (Action映射拦截器):Parameters set by the action mapping are not set/not available through ParameterAware
annotationWorkflow (注解工作流拦截器):利用注解替代XML配置,使用annotationWorkflow拦截器可以使用@After、@Before、@BeforeResult等注解,执行流程为before-execute-beforeResult-after顺序
store (消息存储拦截器):在会话中为Action存储和检索消息、字段错误以及Action错误,该拦截器要求Action实现ValidationAware接口。
二、配置拦截器和拦截器栈
拦截器和拦截器栈外部都有<interceptors></interceptors>标签报位,是它的子标签。interceptors在package下面
配置拦截器比较简单,只需要像向下面那样配置即可
<interceptor name="myinterceptor" class="cn.nju.fxd.interceptor.NameInterceptor">
<!-- 下面的元素可以出现0次,也可以出现无限次,其中,name属性指定需要设定的参数名,可以在对应的拦截器实现类中使用 -->
<param name="name">123456</param>
</interceptor>
拦截器栈由多个拦截器组成,如下面那样
<interceptor-stack name="crudStack">
<interceptor-ref name="params"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
上述两个的配置都是在包下面配置的,也就是package标签。另外,拦截器栈里面也可以包含拦截器栈,比如下面的例子就包含了上面的myinterceptor拦截器和crudStack拦截器栈
<interceptor-stack name="complicatedStack">
<interceptor-ref name="crudStack"/>
<interceptor-ref name="myinterceptor"/>
</interceptor-stack>
有两个时机为拦截器指定参数值,上面的myinterceptor为时机一,即在配置拦截器的时候为其指定参数值,当然也可以在使用拦截器的时候为其指定参数值。比如我们在使用fileUpload拦截器时,为其指定了两个参数,allowedTypes和maximumSize。
三、使用拦截器
一旦定义完拦截器或者拦截器栈,那么你就可以使用拦截器了,使用的方式是在action标签内使用interceptor-ref标签。如下;
<action name="uploadAction" class="cn.nju.fxd.action.UploadAction">
<interceptor-ref name="fileUpload">
<param name="allowedTypes">image/png,image/gif,image/jpeg</param>
<param name="maximumSize">200000</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
<param name="savePath">/upload</param>
<param name="allowType">image/png,image/gif,image/jpeg</param>
<result>/uploadSucc.jsp</result>
<result name="input">/upload.jsp</result>
</action>
上面的例子使用了fileupload拦截器,设置拦截器的过程中使用了两个参数,这两个出参数allowedType和maximumSize覆盖了fileUpload参数。
四、配置默认拦截器
default-interceptor-ref标签
在package包下配置默认拦截器,当action没有显示应用拦截器时,这个拦截器会生效(可以给默认拦截器配置参数;
<package name="" extends="">
<default-interceptor-ref name="">
<param name=""></param>
</default-interceptor-ref>
<action>。。。。。。
</package>
每个包只能有一个默认拦截器,想多个拦截器共同作为默认的?很简单,做成拦截器栈就可以了!
五、实现拦截器类
配置拦截器定义拦截器的时候,我们指定了一个出class参数。这个参数就是我们要实现的拦截器类了。
要实现了拦截器类,我们需要实现Interceptor接口,该接口的类定义如下:
public interface Interceptor extends Serializable {
/**
* Called to let an interceptor clean up any resources it has allocated.
*/
void destroy();
/**
* Called after an interceptor is created, but before any requests are processed using
* {@link #intercept(com.opensymphony.xwork2.ActionInvocation) intercept} , giving
* the Interceptor a chance to initialize any needed resources.
*/
void init();
/**
* Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
* request by the {@link ActionInvocation} or to short-circuit the processing and just return a String return code.
*
* @param invocation the action invocation
* @return the return code, either returned from {@link ActionInvocation#invoke()}, or from the interceptor itself.
* @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}.
*/
String intercept(ActionInvocation invocation) throws Exception;
}
可以看到,我们需要实现3个方法,这三个方法的含义如下:
init():在拦截器被实例化之后,在该拦截器执行拦截之前,会调用该方法。对于每个拦截器,该方法只会被执行一次,因此该方法主要用于初始化资源,数据库连接等
destroy():与init方法对应,执行完拦截之后被调用,用于销毁init()里的资源
intercept():该方法用于实现拦截动作,就像Action里面的execute方法一样,intercept会返回一个字符串作为逻辑视图,不会调用被拦截的Action。
下面写一个简单的拦截器实现:
要拦截的action:
public class DownloadAction extends ActionSupport{
private String inputPath;
private String fileName;
public InputStream getTargetFile() throws Exception{
System.out.println(getFileName());
//String path = ServletActionContext.getServletContext().getRealPath(getInputPath());
return ServletActionContext.getServletContext().getResourceAsStream(getInputPath()+"\\"+fileName);
}
public String getInputPath() {
return inputPath;
}
public void setInputPath(String inputPath) {
this.inputPath = inputPath;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
拦截器代码:
public class SimpleInterceptor implements Interceptor{
private String name;//拦截器名字
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void init() {
// TODO Auto-generated method stub
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
DownloadAction action = (DownloadAction) invocation.getAction();
System.out.println(name+"拦截器的动作-------"+"开始执行action的时间为:"+new Date());
long start=System.currentTimeMillis();
//invoke操作会将控制权转交给下一个拦截器,如果没有,则调用action
String result = invocation.invoke();
System.out.println(name+"拦截器的动作-------"+"执行完成下载action的时间:"+new Date());
long end = System.currentTimeMillis();
System.out.println(name+"拦截器的动作-------"+"执行完成该action的时间长度为:"+(end-start)+"毫秒");
System.out.println("result:"+result);
return result;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
struts.xml中的定义和配置
<package name="upload_download" extends="struts-default">
<interceptors>
<interceptor name="myinterceptor" class="cn.nju.fxd.interceptor.SimpleInterceptor">
<!-- 下面的元素可以出现0次,也可以出现无限次,其中,name属性指定需要设定的参数名,可以在对应的拦截器实现类中使用 -->
<!-- <param name="name">123456</param> -->
</interceptor>
</interceptors>
<action name="download" class="cn.nju.fxd.action.DownloadAction">
<param name="inputPath">\images</param>
<result name="success" type="stream">
<!-- 文件类型 -->
<param name="contentType">application/pdf</param>
<!-- 由getTargetFile()方法返回被下载文件的inputStream -->
<param name="inputName">targetFile</param>
<!-- 指定下载的文件名 -->
<param name="contentDisposition">filename="a.jpg"</param>
<param name="bufferSize">4096</param>
</result>
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="myinterceptor">
<param name="name">简单拦截器</param>
</interceptor-ref>
</action>
</package>
运行即可显示简单的拦截效果。
注意千万不要拼写错误!
六、指定某个拦截方法的拦截器
要实现拦截某个方法,而不是像上面那样拦截所有的方法,有个很简答的方式,就是拦截器类继承MythodFilterinterceptor抽象类。MethodFilerInterceptor实现方法过滤中用到的两个参数
execludeMethods:该参数指定拦截器拒绝拦截的方法列表,多个方法用“,”隔开(支持通配符*,例如add*,表示所有以add开头的方法),如果指定了这个参数拦截器不会拦截指定列表中的方法,就是所谓的黑名单
includeMethods: 该参数指定拦截器需要拦截的方法列表,多个方法用“,”隔开(支持通配符*,例如add*,表示所有以add开头的方法),如果指定了参数,则指定的Action在执行前会被拦截,即白名单。
在struts.xml文档中定义好自定义拦截器后, 就要使用自定义拦截器。
execludeMethods:该参数指定拦截器拒绝拦截的方法列表,多个方法用“,”隔开(支持通配符*,例如add*,表示所有以add开头的方法),如果指定了这个参数拦截器不会拦截指定列表中的方法,就是所谓的黑名单
includeMethods: 该参数指定拦截器需要拦截的方法列表,多个方法用“,”隔开(支持通配符*,例如add*,表示所有以add开头的方法),如果指定了参数,则指定的Action在执行前会被拦截,即白名单。
在struts.xml文档中定义好自定义拦截器后, 就要使用自定义拦截器。
在包内定义拦截器:
<package....>
<interceptors>
<interceptor name="myinterceptor" class="....">
</interceptor>
</interceptors>
</package>
在action内使用拦截器
<action .....>
<result.....></result>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="myinterceptor"></interceptor-ref>
</action>
拦截器类继承抽象类MethodFilterInterceptor
public class MyMethodInterceptor extends MethodFilterInterceptor{
protected String doIntercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
System.out.println("进入MyMethodInterceptor方法拦截器!!!!!!!!!!!!!");
Map session = invocation.getInvocationContext().getSession();
String name = (String) session.get("uname");
if (name != null) {
return invocation.invoke();
}
return "input";
}
}
在Struts.xml中配置拦截器,不拦截login方法
<?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.i18n.encoding" value="UTF-8"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<!-- <constant name="struts.action.extension" value="action,abc"></constant>-->
<package abstract="true" name="pk1" namespace="/" extends="struts-default"></package>
<package name="k2" extends="pk1">
<interceptors>
<interceptor name="method1" class="org.interceptors.MyMethodInterceptor">
<param name="excludeMethods">login</param>
</interceptor>
<!--定义拦截器栈-->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="method1"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!--设置默认拦截器-->
<!-- <default-interceptor-ref name="myStack"></default-interceptor-ref>-->
<action name="login_*_*" class="org.hzy.Actions.LoginAction" method="{1}">
<interceptor-ref name="myStack"></interceptor-ref>
<result name="success" type="chain">{2}</result>
<result name="input">index.jsp</result>
<result name="error">/WEB-INF/Jsp/error.jsp</result>
</action>
<action name="query_*" class="org.hzy.Actions.QueryAction" method="{1}">
<result>/WEB-INF/Jsp/show.jsp</result>
</action>
<action name="upload_*" class="org.hzy.Actions.FileUploadAction" method="{1}">
<result>/WEB-INF/Jsp/show.jsp</result>
</action>
</package>
</struts>
七、拦截结果的监听器
首先写一个监听器类,实现PreResultListener接口
public class MyPreResultListener implements PreResultListener{
@Override
public void beforeResult(ActionInvocation invocation, String resultCode) {
// TODO Auto-generated method stub
System.out.println("返回的逻辑视图为:"+resultCode);
}
}
然后在拦截器中,添加这个监听器类
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
//注册结果监听器
invocation.addPreResultListener(new MyPreResultListener());
DownloadAction action = (DownloadAction) invocation.getAction();
System.out.println(name+"拦截器的动作-------"+"开始执行action的时间为:"+new Date());
long start=System.currentTimeMillis();
//invoke操作会将控制权转交给下一个拦截器,如果没有,则调用action
String result = invocation.invoke();
System.out.println(name+"拦截器的动作-------"+"执行完成下载action的时间:"+new Date());
long end = System.currentTimeMillis();
System.out.println(name+"拦截器的动作-------"+"执行完成该action的时间长度为:"+(end-start)+"毫秒");
System.out.println("result:"+result);
return result;
}
运行即可
八、使用拦截器完成权限控制(登录控制)
第一步,实现一个拦截器类,拦截未登录的用户
public class LoginAuthorityInterceptor extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// TODO Auto-generated method stub
ActionContext ctx = invocation.getInvocationContext();
Map session = ctx.getSession();
String user = (String) session.get("username");
if(user!=null&&user.equals("fanxuande")){
return invocation.invoke();
}
ctx.put("tip", "您还没有登录,请先登录");
return Action.LOGIN;
}
}
第二步,在struts.xml定义该拦截器
<interceptor name="loginAuthority" class="cn.nju.fxd.interceptor.LoginAuthorityInterceptor"/>
第三步,在action中使用该拦截器,由于该拦截器返回了一个Action.LOGIN,所以也需要全局results
<interceptor-ref name="loginAuthority"/>
<global-results >
<result name="login">/login.jsp</result>
</global-results>
注意,这两个再打位置不同,Interceptor-ref在action内部,global-results在package内部。