发布时间:2025-12-09 15:54:09 浏览次数:11
拦截器(Interceptor)是Struts2最强大的特性之一,它是一种可以让你在Action执行之前和Result执行之后进行一些功能处理的机制。来回顾一下官方给出的Struts2系统架构图中关于拦截器的部分,如下图所示:
这个图清晰的描述出了拦截器的运行地位,就是用来负责在Action执行之前和Result执行之后处理一些功能的类。也就是说,上图示意了有3个拦截器的类,分别是Interceptor1、Interceptor2和Interceptor3,它们分别执行不同的功能处理,而运行的时机就是在Action执行之前和Result执行之后。
拦截器方法都是通过代理的方式来调用的。Struts 2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。事实上,我们之所以能够如此灵活地使用拦截器,完全归功于动态代理的使用。动态代理是代理对象根据客户的需求做出不同的处理。对于客户来说,只要知道一个代理对象就行了。
那Struts2中,拦截器是如何通过动态代理被调用的呢?当Action请求到来的时候,会由系统的代理生成一个Action的代理对象,由这个代理对象调用Action的execute()或指定的方法,并在struts.xml中查找与该Action对应的拦截器。如果有对应的拦截器,就在Action的方法执行前(后)调用这些拦截器;如果没有对应的拦截器则执行Action的方法。其中系统对于拦截器的调用,是通过ActionInvocation来实现的
拦截器能实现很多功能,这里先撇开具体功能不谈,从设计和程序结构上来看看,拦截器有些什么优点:
备注:有web开发经验的朋友看到这里,一定会说,这不就类似于Filter么?过滤器也可以做这些事啊。没错,拦截器和Filter确实有相似之处,但是Interceptor相比于Filter具有更强大的功能,比如拦截器与Servlet的API无关,比如拦截器可以访问到值栈等等。
在之前的示例中,在运行Action的execute方法的时候,会发现Action的属性已经有值了,而且这些值跟用户请求中的参数值时一样的。这说明,在execute方法之前,有人偷偷的把用户请求中的参数值和Action的属性做了一个对应,并且把请求中的参数赋值到了Action的属性上,这个功能就是由缺省配置的拦截器来实现的。这些缺省配置的拦截器,称之为预定义的拦截器。我们还可以编写自己需要的拦截器类,称之为自定义拦截器,它的具体功能就根据需要,由我们自行实现了。
Struts2有默认的拦截器配置,也就是说,虽然我们没有主动去配置任何关于拦截器的东西,但是Struts2会使用默认引用的拦截器。由于Struts2的默认拦截器声明和引用都在这个Struts-default.xml里面,因此我们需要到这个文件的struts-default包里去看一下。定义如下:
<interceptors> <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/> <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/> <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/> <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/> <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" /> <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" /> <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" /> <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/> <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/> <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/> <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/> <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/> <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/> <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/> <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/> <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/> <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/> <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/> <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/> <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/> <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/> <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/> <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/> <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" /> <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" /> <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" /> <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" /> <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" /> <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" /> <interceptor name="deprecation" class="org.apache.struts2.interceptor.DeprecationInterceptor" /> <!-- Basic stack --> <interceptor-stack name="basicStack"> <interceptor-ref name="exception"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="deprecation"/> </interceptor-stack> <!-- Sample validation and workflow stack --> <interceptor-stack name="validationWorkflowStack"> <interceptor-ref name="basicStack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack> <!-- Sample file upload stack --> <interceptor-stack name="fileUploadStack"> <interceptor-ref name="fileUpload"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <!-- Sample model-driven stack --> <interceptor-stack name="modelDrivenStack"> <interceptor-ref name="modelDriven"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <!-- Sample action chaining stack --> <interceptor-stack name="chainStack"> <interceptor-ref name="chain"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <!-- Sample i18n stack --> <interceptor-stack name="i18nStack"> <interceptor-ref name="i18n"/> <interceptor-ref name="basicStack"/> </interceptor-stack> <interceptor-stack name="paramsPrepareParamsStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="i18n"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="params"> <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param> </interceptor-ref> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack><interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">^class\..*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param> </interceptor-ref><interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="debugging"/> <interceptor-ref name="deprecation"/> </interceptor-stack><interceptor-stack name="completeStack"> <interceptor-ref name="defaultStack"/> </interceptor-stack> <interceptor-stack name="executeAndWaitStack"> <interceptor-ref name="execAndWait"> <param name="excludeMethods">input,back,cancel</param> </interceptor-ref> <interceptor-ref name="defaultStack"/> <interceptor-ref name="execAndWait"> <param name="excludeMethods">input,back,cancel</param> </interceptor-ref> </interceptor-stack></interceptors><default-interceptor-ref name="defaultStack"/>这个拦截器是必不可少的,因为就是由它偷偷的把请求参数设置到相应的Action的属性去的,并自动进行类型转换。
staticParams拦截器
将struts.xml配置文件里定义的Action参数,设置到对应的Action实例中,Action参数使用<param>标签,是<action>标签的子元素。struts.xml的示例如下:
这要求Action中一定要有一个account的属性,并有相应的getter/setter方法。运行的时候,Action的account属性在初始化过后,会接到这里的赋值test。
注意:params拦截器和staticParams拦截器都会为Action的属性赋值,如果碰到了都要赋同一个值呢,比如request里面有account参数,而struts.xml中也有account参数,最终的值是谁?其实是Action初始化过后,就会把struts.xml中配置的数据设置到Action实例中相应的属性上去。然后,把用户请求的数据设置到Action实例中相应的属性上去。很明显最后的值是用户请求中account的数据。
该拦截器可以记录ActionInvocation余下部分执行的时间,并做为日志信息记录下来,便于寻找性能瓶颈。也就是说,该拦截器可以记录Action运行的时间。
在日志信息中输出要执行的Action信息 ,这样,在调试的时候,就能很快的定位到这个对应的Action了
<interceptor>元素用来定义一个拦截器,这里仅仅是一个定义,还没有任何一个Action来引用它。里面的name属性作为唯一标志,而class属性就是这个拦截器的实现类。拦截器的实现类都应该是com.opensymphony.xwork2.interceptor.Interceptor这个接口的实现类。
<interceptor-stack>定义了一个拦截器栈,这个栈中可以引用其他已经定义好的拦截器。拦截器栈简化了动作类Action在引用拦截器时的操作。
因为大多数动作类Action在引用拦截器的时候都不会仅仅引用一个拦截器,而是引用一组拦截器,而多个动作类Action大概又会引用同一组拦截器,这时候,为了引用的方便,可以把多个拦截器组合成一个拦截器栈。Action在引用的时候,只需要引用这个拦截器栈就可以了,而不是引用每一个拦截器。下面看一下struts-2.1.dtd对于<action>元素的定义:
<!ELEMENT action (param|result|interceptor-ref|exception-mapping)*>
action元素后面出现的interceptor-ref子元素后面用*来修饰,这说明一个action元素可以有不限个数的interceptor-ref子元素。那么在<action>元素中,如何使用<interceptor-ref>子元素呢?其实很简单,只需要在<action>元素中,配置需要的<interceptor-ref>子元素就可以了,<interceptor-ref>子元素里面配置需要使用的拦截器的名称,比如:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <param name="account">test</param> <result>/s2impl/welcome.jsp</result> <interceptor-ref name="staticParams"/> <interceptor-ref name="defaultStack"/> </action><interceptor-ref>子元素中的name,不仅仅可以是一个已经定义好的拦截器的名称,还可以是一个已经定义好的拦截器栈的名称。上面的示例,就引用了一个拦截器和一个拦截器栈。
<default-interceptor-ref>在包上声明包内所有的Action都使用的拦截器
先看一下struts-2.1.dtd对于<package>元素的定义:
其实,在配置自己的package的时候所扩展的struts-default包里面,就已经定义了一个<default-interceptor-ref>,在Struts-default.xml中定义的struts-default包内,有如下定义:
<default-interceptor-ref name="defaultStack"/>
正是因为有这个定义,我们都没有主动去配置拦截器,但实际上,是有拦截器在运行并执行很重要的工作,只不过是使用的默认的拦截器,我们不知道罢了。
在学习了预定义拦截器的配置使用之后,接下来看看<action>元素引用拦截器的调用顺序。在拿到一个动作类的声明<action>元素后,如何找到它引用的拦截器呢?
特别注意:这三个地方的定义是覆盖的关系,什么意思呢?就是如果<action>里面声明了拦截器引用,那么就以它的为准,其他的定义就无效了。也即是<action>里面的拦截器引用声明会覆盖<package>里面的缺省拦截器声明,而<package>里面的缺省拦截器声明又会覆盖父包的<package>里面的缺省拦截器声明,以此类推。
使用execAndWait拦截器可以在等待较长时间的后台处理中增加等待页面。
struts.xml主要部分
action主要部分
public String queryall(){for (int i = 0; i < 500000; i++) {System.out.println(i);}user = "你好";this.setListData();return Action.SUCCESS;}jsp页面取值部分(rs.jsp)
List list = (List)request.getAttribute("listall"); 取值OK等待页面部分(wait.jsp)
<%@ page contentType="text/html; charset=GBK" language="java"errorPage=""%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE HTML><html><head><meta http-equiv=Content-Type content="text/html;charset=gbk"><META HTTP-EQUIV="Refresh" content="2;url=<s:url includeParams="all"/>"/> <title> 正在查询,请稍等...</title><style type="text/css">.query_hint{border:5px solid #939393;width:250px;height:50px;line-height:55px;padding:0 20px;position:absolute;left:50%;margin-left:-140px;top:50%;margin-top:-40px;font-size:15px;color:#333;font-weight:bold;text-align:center;background-color:#f9f9f9;}.query_hint img{position:relative;top:10px;left:-8px;}</style></head><body><p id="query_hint" class="query_hint"><img src="http://files.cnblogs.com/ningvsban/waiting.gif" />正在查询,请稍<s:url includeParams="all"/>等...</p><!--<a href="<s:url includeParams="all" />"> 点这里 </a> 如果没有自动跳转请点击这里.--></body></html>关于<meta http-equiv="refresh" content="0;url= "/>的几点说明:
什么是自定义的拦截器,所谓自定义的拦截器,就是由我们自己定义并实现的拦截器,而不是由Struts2定义好的拦截器。虽然Struts2的预定义拦截器已经满足了大多数情况的需要。但在有些时候,我们可能会根据项目的实际需要而自定义一些拦截器,来实现一些特别的功能
在Struts2里面,要实现自定义的拦截器是非常简单的,只要写一个实现Interceptor接口的类就可以了。也就是说,所有的拦截器都要实现
com.opensymphony.xwork2.interceptor.Interceptor接口,这个接口中定义如下:
方法的基本说明如下:
继承AbstractInterceptor类:无法通过<param name="includeMethods">拦截的方法</param>参数配置包含拦截的方法
通过<param name="excludeMethods">不拦截的方法</param>来控制拦截或不拦截的方法
继承MethodFilterInterceptor类:可以通过includeMethods和excludeMethods属性配置拦截或不拦截的方法
public abstract class MethodFilterInterceptor extends AbstractInterceptor {protected Set<String> excludeMethods = Collections.emptySet();protected Set<String> includeMethods = Collections.emptySet();....}先来个最简单的,就是在Action运行之前,和Result运行之后输出一点信息,当然,有实际功能需求的时候,就写成实际功能的处理代码了,示例代码如下:
package cn.javass.hello.struts2impl.action;import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class MyInterceptor implements Interceptor{ public void destroy() { System.out.println("MyInterceptor 销毁"); } public void init() { System.out.println("MyInterceptor 初始化"); } public String intercept(ActionInvocation invocation) throws Exception { System.out.println("在acton执行之前"); String result = invocation.invoke(); System.out.println("在Result运行之后"); return result; } }可以看到,这个Interceptor的init方法和destroy方法只是输出了一句信息,它的intercept方法用来执行响应,在invocation.invoke();这句话之前和之后分别输出了一句信息。最后返回的result,就是invocation.invoke()的返回值。
HelloWorldAction这个类不用修改
需要到struts.xml里面配置拦截器的声明和引用,示例如下:
运行测试一下,后台输出:
在acton执行之前用户输入的参数为===account=11,password=11111111111,submitFlag=login在Result运行之后2014-5-18 21:18:46 com.opensymphony.xwork2.interceptor.TimerInterceptor info信息: Executed action [//helloworldAction!execute] took 152 ms.在实际开发中,一个常见的功能要求是:有很多操作都需要登录后才能操作,如果操作的时候还没有登录,那么通常情况下会要求跳转回到登录页面。
在具体实现之前,先来考虑几个问题:
实现满足要求的拦截器,示例代码如下:
package cn.javass.hello.struts2impl.action;import java.util.Map; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class SessionCheckInterceptor implements Interceptor{ //设置参数 private String sessionAttribute; private String reloginResult; public void setSessionAttribute(String sessionAttribute) { this.sessionAttribute = sessionAttribute; } public void setReloginResult(String reloginResult) { this.reloginResult = reloginResult; } public void destroy() { } public void init() { } public String intercept(ActionInvocation invocation) throws Exception { //读取Session Map<String, Object> session = invocation.getInvocationContext().getSession(); //判断Session中是否有相应的attribute if (session.containsKey(sessionAttribute)){ String resultCode = invocation.invoke(); return resultCode; }else{ return reloginResult; } } }在intercept方法中,先读取session中指定的attribute,具体读取哪个attribute由参数从外界传进来,然后判断Session中是否存在这个attribute,如果有则继续执行后续的拦截器、Action和Result,如果没有则跳转到指定的Result所对应的页面,具体跳转到哪个Result也是由参数从外界传进来的。
拦截器配置,配置示例如下:
<package name="helloworld" extends="struts-default"> <interceptors> <interceptor name="testInteceptor" class="cn.javass.hello.struts2impl.action.MyInterceptor"/> <interceptor name="myLogger" class="cn.javass.hello.struts2impl.action.LoggerInterceptor"/><interceptor name="loginChecker" class="cn.javass.hello.struts2impl.action.SessionCheckInterceptor"/> <interceptor-stack name="myStack"> <interceptor-ref name="timer"/> <interceptor-ref name="testInteceptor"/><interceptor-ref name="myLogger"/> <interceptor-ref name="loginChecker"> <param name="sessionAttribute">login_user</param> <param name="reloginResult">login</param> </interceptor-ref> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="myStack"/> <global-results> <result name="math-exception">/${folder}/error.jsp</result> <result name="login">/s2impl/login.jsp</result> </global-results> <global-exception-mappings> <exception-mapping result="math-exception" exception="java.lang.ArithmeticException"/> <exception-mapping result="math-exception" exception="java.lang.Exception"/> </global-exception-mappings> <action name="helloworldAction" class="cn.javass.hello.struts2impl.action.HelloWorldAction"> <result name="toWelcome">/${folder}/welcome.jsp</result> </action> </package>Struts2自带的logger拦截器只是打印出了Action所对应的URL以及执行的方法名称,这对实际开发来说是肯定不够的。实际开发中为了调试方便,要记录的信息比较多,通常需要把这次请求相关的几乎所有信息都打印出来,比如:
要访问哪个Action类
要访问这个Action类的哪个方法
打印出这次请求中所有的request中的parameter参数
这次请求最后跳转到哪个页面。
如果我们现在就要在拦截器中实现这样的功能,该怎么实现呢?
参考资料:http://www.iteye.com/topic/1124526