Struts拦截器
默认的Interceptor堆栈旨在满足大多数应用程序的需求。大多数应用程序并不需要添加拦截器或改变拦截器栈。
许多行动都有共同的担忧。某些操作需要输入验证。其他操作可能需要预处理文件上载。另一个行动可能需要保护双重提交。许多操作需要在页面显示之前预先填充的下拉列表和其他控件。
该框架使用“拦截器”策略可以轻松地分享这些问题的解决方案。当您请求映射到“操作”的资源时,框架将调用Action对象。但是,在执行Action之前,调用可能会被另一个对象拦截。Action执行后,可以再次拦截调用。不出所料,我们将这些对象称为“拦截器”。
Introducing Interceptors
Struts 2框架在执行Action之前和之后完成的任务由Struts 2拦截器完成。拦截器是Struts 2核心jar中包含的标准Java类,它们按特定顺序执行。
在示例应用程序中,struts.xml中有一个包节点。包节点的属性为extends,其值为“struts-default”。值“struts-default”向框架标识将在该包中的Actions之前和之后执行的特定拦截器堆栈。
有时,Struts 2默认的拦截器堆栈并不是特定操作所需的。您可能希望使用不属于Struts 2默认堆栈的拦截器。对于单个Action或整个Actions包,您可以指定Action或包应使用不同拦截器堆栈。下面是如何指定寄存器Action 除了默认堆栈提供的拦截器外还应使用记录器和定时器拦截器。
拦截器可以在调用Action之前和之后执行代码。框架的大多数核心功能都是作为拦截器实现的。双重提交警卫,类型转换,对象填充,验证,文件上传,页面准备等功能都是在拦截器的帮助下实现的。每个Interceptor都是可插拔的,因此您可以确定Action需要支持哪些功能。
拦截器可以基于每个动作进行配置。您自己的自定义拦截器可以与框架捆绑的拦截器混合和匹配。拦截器为Action类“设置阶段”,在Action执行之前做了很多“繁重的工作”。
行动生活
在某些情况下,由于双重提交或验证失败,Interceptor可能会阻止Action触发。拦截器还可以在执行之前更改Action的状态。
拦截器在堆栈中定义,指定执行顺序。在某些情况下,堆栈上的拦截器的顺序非常重要。
为动作指定特定拦截器
<action name="register" class="org.apache.struts.register.action.Register" method="execute">
<interceptor-ref name="timer" />
<interceptor-ref name="logger" />
<interceptor-ref name="defaultStack">
<param name="exception.logEnabled">true</param>
<param name="exception.logLevel">ERROR</param>
</interceptor-ref>
<result name="success">thankyou.jsp</result>
<result name="input">register.jsp</result>
</action>
记录器拦截器记录Action执行的开始和结束。计时器拦截器记录执行Action的时间量(以毫秒为单位)。这两个拦截器一起使用可以为开发人员提供有用的反馈。
在上面的代码示例中,请注意三个拦截器-ref节点。每个都有name属性的值。对于寄存器操作,我们指示使用的框架timer,logger和defaultStack拦截器。在defaultStack 通常一个动作执行的所有拦截器。
我怎么知道使用timer的值作为name属性,甚至还有一个定时器拦截器?在Struts 2文档中的拦截器网页上有Struts 2框架附带的拦截器列表以及每个拦截器的名称值。
我怎么知道定时器拦截器不是拦截器的defaultStack的一部分?再次在拦截器文档网页上列出了哪些拦截器属于defaultStack。
注意param节点。这些节点用于为Exception Interceptor的setLogEnabled和setLogLevel方法提供值。提供true和ERROR的值将导致Struts 2框架记录未被应用程序代码捕获的任何异常,并在ERROR级别记录这些异常。
为包指定特定拦截器
<package name="basicstruts2" extends="struts-default" >
<interceptors>
<interceptor-stack name="appDefault">
<interceptor-ref name="timer" />
<interceptor-ref name="logger" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="appDefault" />
<!-- rest of package omitted -->
</package>
在上面的代码,我们使用拦截器节点来定义拦截器的新的堆栈,其包括timer,logger,和defaultStack拦截器。我们为这个新的拦截器堆栈命名为appDefault。然后我们使用该default-interceptor-ref 节点指定对于此包节点内定义的所有Actions appDefault,将使用拦截器堆栈。因此timer,logger将为此包中的每个Action执行和拦截器。
请注意,在这两个示例中,我们仍然通过将defaultStack包含为其中一个interceptor-ref节点来执行所有其他拦截器。指定要用于Action或包的拦截器时,只执行那些拦截器。所以,如果在这个例子中,我们已经离开了interceptor-ref的defaultStack只有logger和timer拦截器就会执行。
创建自己的拦截器
除了指定自己的拦截器堆栈之外,您还可以编写自己的新拦截器并将其添加到执行的堆栈中。Struts Writing Interceptors指南解释了如何执行此操作。例如,您可以创建自己的拦截器来处理身份验证和授权。
配置拦截器
在struts.xml
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="timer" class=".."/>
<interceptor name="logger" class=".."/>
</interceptors>
<action name="login" class="tutorial.Login">
<interceptor-ref name="timer"/>
<interceptor-ref name="logger"/>
<result name="input">login.jsp</result>
<result name="success" type="redirectAction">/secure/home</result>
</action>
</package>
堆叠拦截器
对于大多数Web应用程序,我们发现自己想要一遍又一遍地应用同一组拦截器。我们可以使用拦截器堆栈将这些拦截器捆绑在一起,而不是重复相同的拦截器列表。
在struts.xml
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="timer" class=".."/>
<interceptor name="logger" class=".."/>
<interceptor-stack name="myStack">
<interceptor-ref name="timer"/>
<interceptor-ref name="logger"/>
</interceptor-stack>
</interceptors>
<action name="login" class="tutuorial.Login">
<interceptor-ref name="myStack"/>
<result name="input">login.jsp</result>
<result name="success" type="redirectAction">/secure/home</result>
</action>
</package>
从内部看struts-default.xml,我们可以看到它是如何完成的。
框架拦截器
拦截器类也使用Struts配置文件中指定的键值对定义。下面指定的名称在struts-default.xml中指定。如果扩展struts-default 包,则可以使用以下名称。否则,必须在包中使用标记中指定的名称类对定义它们。
方法过滤
MethodFilterInterceptor是一个抽象,Interceptor用作拦截器的基类,它将根据指定的包含/排除方法列表根据方法名称过滤执行。
可设置的参数如下: – excludeMethods – 要从拦截器处理中排除的方法名称 – includeMethods – 要包含在拦截器处理中的方法名称
如果includeMethods和excludeMethods中都有方法名称,则它将被视为包含的方法:includeMethods优先于excludeMethods。
扩展此功能的拦截器包括: – TokenInterceptor – TokenSessionStoreInterceptor – DefaultWorkflowInterceptor -ValidationInterceptor
拦截器参数覆盖
拦截器的参数可以通过以下方式覆盖:
方法1:
<action name="myAction" class="myActionClass">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="params"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">myValidationExcludeMethod</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
</action>
方法2:
<action name="myAction" class="myActionClass">
<interceptor-ref name="defaultStack">
<param name="validation.excludeMethods">myValidationExcludeMethod</param>
<param name="workflow.excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
</action>
在第一种方法中,复制整个默认堆栈,然后相应地更改参数。
在第二种方法中,interceptor-ref引用现有的拦截器堆栈,即defaultStack在此示例中,并覆盖validator和workflow拦截器excludeMethods属性。请注意,在param 标记中,name属性包含一个点(。),点之前的单词(。)指定要覆盖其参数的拦截器名称,而点(。)之后的单词指定参数本身。语法如下:
<interceptor-name>.<parameter-name>
还要注意,在这种情况下,interceptor-refname属性用于指示一个拦截器堆栈,它有意义,好像它指的是拦截器本身,它只是使用上面描述的方法1。
方法3:
<interceptors>
<interceptor-stack name="parentStack">
<interceptor-ref name="defaultStack">
<param name="params.excludeParams">token</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="parentStack"/>
拦截器参数覆盖继承
参数覆盖不会在拦截器中继承,这意味着将使用最后一组重写参数。例如,如果堆栈覆盖“postPrepareParameterFilter”拦截器的参数“defaultBlock”,则:
<interceptor-stack name="parentStack">
<interceptor-ref name="postPrepareParameterFilter">
<param name="defaultBlock">true</param>
</interceptor-ref>
</interceptor-stack>
并且操作会覆盖“postPrepareParameterFilter”的“allowed”:
<package name="child2" namespace="/child" extends="parentPackage">
<action name="list" class="SomeAction">
<interceptor-ref name="parentStack">
<param name="postPrepareParameterFilter.allowed">myObject.name</param>
</interceptor-ref>
</action>
</package>
然后,只有“allowed”将被覆盖该动作中的“postPrepareParameterFilter”拦截器,其他参数将为null。
懒惰的参数
可以使用在动作调用期间评估的参数来定义拦截器。在这种情况下,拦截器必须用WithLazyParams接口标记 。这必须是开发人员的决定,因为拦截器必须知道在调用期间设置这些参数,而不是在正常情况下创建拦截器时。
参数被评估为从作为顶级对象的动作开始的任何其他表达式。
<action name="LazyFoo" class="com.opensymphony.xwork2.SimpleAction">
<result name="success">result.jsp</result>
<interceptor-ref name="lazy">
<param name="foo">${bar}</param>
</interceptor-ref>
</action>
public class MockLazyInterceptor extends AbstractInterceptor implements WithLazyParams {
private String foo = "";
public void setFoo(String foo) {
this.foo = foo;
}
public String intercept(ActionInvocation invocation) throws Exception {
....
return invocation.invoke();
}
}
请注意,当想要访问通过请求传递的参数时,拦截器的顺序可能很重要,因为这些参数是由参数拦截器设置的 。
拦截器执行顺序
拦截器提供了一种在处理之前/之后进行包装的极好方法。这个概念减少了代码重复(想想AOP)。
<interceptor-stack name="xaStack">
<interceptor-ref name="thisWillRunFirstInterceptor"/>
<interceptor-ref name="thisWillRunNextInterceptor"/>
<interceptor-ref name="followedByThisInterceptor"/>
<interceptor-ref name="thisWillRunLastInterceptor"/>
</interceptor-stack>
实现的拦截器com.opensymphony.xwork2.interceptor.PreResultListener将在Action执行之后但在Result执行之前运行。
thisWillRunFirstInterceptor
thisWillRunNextInterceptor
followedByThisInterceptor
thisWillRunLastInterceptor
MyAction1
MyAction2 (chain)
MyPreResultListener
MyResult (result)
thisWillRunLastInterceptor
followedByThisInterceptor
thisWillRunNextInterceptor
thisWillRunFirstInterceptor