在默认情况下,如果我们为某个Action定义了拦截器,则这个拦截器会拦截该Action内的所有方法。但有时我们不想拦截所有的方法,只需要拦截指定方法,此时就需要使用Struts 2拦截器的方法过滤特性。
为了实现方法过滤的特性,Struts 2提供了一个MethodFilterInterceptor类,该类是AbstractInterceptor类的子类,如果用户需要自己实现的拦截器支持方法过滤特性,则应该继承MethodFilterInterceptor。
MethodFilterInterceptor类重写了AbstractInterceptor类的intercept(ActionInvocation invocation)方法,但提供了一个doIntercept(ActionInvocation invocation)抽象方法。从这种设计方式可以看出,MethodFilterInterceptor类的intercept已经实现了对Action的拦截行为(只是实现了方法过滤的逻辑),但真正的拦截逻辑还需要开发者提供,也就是通过回调doIntercept方法实现。可见,如果用户需要实现自己的拦截逻辑,则应该重写doIntercept(ActionInvocation invocation)方法。
下面是一个简单的方法过滤的示例应用,方法过滤的拦截器代码如下。
//拦截方法的拦截器,应该继承MethodFilterInterceptor抽象类
public class MyFilterInterceptor
extends MethodFilterInterceptor
{
//简单拦截器的名字
private String name;
//为该简单拦截器设置名字的setter方法
public void setName(String name)
{
this.name = name;
}
//重写doIntercept方法,实现对Action的拦截逻辑
public String doIntercept(ActionInvocation invocation)
throws Exception
{
//取得被拦截的Action实例
LoginAction action = (LoginAction)invocation.getAction();
//打印执行开始的时间
System.out.println(name + " 拦截器的动作---------"
+ "开始执行登录Action的时间为:" + new Date());
//取得开始执行Action的时间
long start = System.currentTimeMillis();
//执行该拦截器的后一个拦截器,或者直接指定Action的execute方法
String result = invocation.invoke();
//打印执行结束的时间
System.out.println(name + " 拦截器的动作---------"
+ "执行完登录Action的时间为:" + new Date());
long end = System.currentTimeMillis();
//打印执行该Action所花费的时间
System.out.println(name + " 拦截器的动作---------"
+ "执行完该Action的事件为" + (end - start) + "毫秒");
return result;
}
}
从上面的代码中可以看出,上面拦截器的拦截逻辑与前面简单拦截器的拦截逻辑相似,只是之前是需要重写intercept方法,现在是重写doIntercept方法。
实际上,实现方法过滤的拦截器与实现普通拦截器并没有太大的区别,只需要注意两个地方:实现方法过滤的拦截器需要继承MethodFilterInterceptor抽象类,并且重写doIntercept方法定义对Action的拦截逻辑。
在MethodFilterInterceptor方法中,额外增加了如下两个方法:
q public void setExcludeMethods(String excludeMethods):排除需要过滤的方法——设置方法“黑名单”,所有在excludeMethods字符串中列出的方法都不会被拦截。
q public void setIncludeMethods(String includeMethods):设置需要过滤的方法——设置方法“白名单”,所有在includeMethods字符串中列出的方法都会被拦截。
注意:如果一个方法同时在excludeMethods和includeMethods中列出,则该方法会被拦截。
因为MethodFilterInterceptor类包含了如上的两个方法,则该拦截器的子类也会获得这两个方法。可以在配置文件中指定需要被拦截,或者不需要被拦截的方法。
方法过滤示例应用的配置片段如下:
<!-- 配置本系统所使用的包 -->
<package name="lee" extends="struts-default">
<!-- 应用所需使用的拦截器都在该元素下配置 -->
<interceptors>
<!-- 配置mysimple拦截器 -->
<interceptor name="mysimple"
class="org.crazyit.app.interceptor.MyFilterInterceptor">
<!-- 为拦截器指定参数值 -->
<param name="name">拦截方法的拦截器</param>
</interceptor>
</interceptors>
<action name="loginPro" class="org.crazyit.app.action.LoginAction">
<result name="error">/WEB-INF/content/error.jsp</result>
<result name="success">/WEB-INF/content/welcome.jsp</result>
<!-- 配置系统的默认拦截器 -->
<interceptor-ref name="defaultStack"/>
<!-- 应用自定义的mysimple拦截器 -->
<interceptor-ref name="mysimple">
<!-- 重新指定name属性的属性值 -->
<param name="name">改名后的拦截方法过滤拦截器</param>
<!-- 指定execute方法不需要被拦截 -->
<param name="excludeMethods">execute</param>
</interceptor-ref>
</action>
<action name="*">
<result>/WEB-INF/content/{1}.jsp</result>
</action>
</package>
上面配置文件的粗体字代码通过excludeMethods属性指定了execute方法无须被拦截,如果浏览者在浏览器中再次向login的Action发送请求,在Tomcat控制台将看不到任何输出,表明该拦截器没有拦截Action的execute方法。
如果需要同时指定多个方法不被该拦截器拦截,则多个方法之间以英文逗号(,)隔开。看如下的配置片段:
<interceptor-ref name="myfilter">
<!-- 重新指定name属性的属性值 -->
param name="name">改名后的方法过滤拦截器</param>
<!-- 指定execute和haha方法不需要被拦截 -->
<param name="excludeMethods">execute,haha</param>
</interceptor-ref>
上面粗体字代码指定execute和haha方法都不会被myfilter拦截器拦截。
如果excludeMethods参数和includeMethods参数同时指定了一个方法名,则拦截器会拦截该方法。看如下的配置片段:
<interceptor-ref name="myfilter">
<!-- 重新指定name属性的属性值 -->
param name="name">改名后的方法过滤拦截器</param>
<!-- 指定execute和haha方法不需要被拦截 -->
<param name="excludeMethods">execute,haha</param>
<!-- 指定execute方法需要被拦截 -->
<param name="includeMethods">execute</param>
</interceptor-ref>
上面配置片段通过excludeMethods参数指定了execute和haha方法不需要被拦截,又通过includeMethods参数指定了execute方法需要拦截——二者冲突!以includeMethods参数指定的取胜,即拦截器会拦截execute方法。
Struts 2中提供了这种方法过滤的拦截器有如下几个:
q TokenInterceptor
q TokenSessionStoreInterceptor
q DefaultWorkflowInterceptor
q ValidationInterceptor