前言
在这篇《Struts2学习之拦截器机制》文章中,我详细的总结了Struts2中的拦截器,以及如何使用拦截器。有的时候,Struts2框架提供的那些内建的拦截器无法满足我们的功能需求时,这个时候我们就需要手动开发自己的拦截器了。这篇文章就来总结如何定制开发一个拦截器。
实现拦截器类
要开发我们自己的拦截器,需要实现com.opensymphony.xwork2.interceptor.Interceptor
接口。该接口代码如下:
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
方法名称 | 方法说明 |
---|---|
init | 在该拦截器被实例化之后,拦截器执行拦截之前,系统将回调该方法。对于每个拦截器而言,其init方法只执行一次。因此该方法主要用于初始化资源 |
destroy | 该方法与init方法对应。在拦截器实例被销毁之前,系统将回调该拦截器的destroy方法,该方法用于销毁在init方法里打开的资源 |
intercept | 该方法是用户需要实现的拦截动作。就像Action的execute方法一样,intercept方法会返回一个字符串作为逻辑视图。如果该方法直接返回了一个字符串,系统将会跳转到该逻辑视图对应的实际视图资源,不会调用被拦截的Action。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过调用该参数的invoke方法,将控制权转给下一个拦截器,或者转给Action的execute方法 |
Interceptor
只是一个接口,为了更方便的使用,Struts2还提供了一个AbstractInterceptor
类,该类提供了一个init
和destroy
方法的空实现,如果我们实现的拦截器不需要打开资源,则可以无需实现这两个方法。下面就是我们继承AbstractInterceptor
类,实现的一个简单的拦截器。
public class InterceptDemo extends AbstractInterceptor
{
// 拦截器的名字
private String name;
public void setName(String name)
{
this.name = name;
}
public String intercept(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();
System.out.println(name + "拦截器的动作----------------" + "执行完该Action的事件为" + (end - start) + "毫秒");
return result;
}
}
上面实现了超级简单的拦截器,其中有一个非常重要的参数ActionInvocation
,我们通过这个参数可以获得被拦截的Action实例,一旦取得了Action实例,几乎获得了全部的控制权,接下来就可以“胡作非为”了。
使用拦截器
使用拦截器需要以下两个步骤:
- 通过
<interceptor .../>
元素来定义拦截器 - 通过
<interceptor-ref .../>
元素来使用拦截器
对于上面的拦截器,我们的struts.xml配置如下:
<struts>
<package name="InterceptDemo" extends="struts-default">
<interceptors>
<interceptor name="InterceptDemo" class="com.jellythink.practise.InterceptDemo">
<param name="name">演示拦截器</param>
</interceptor>
</interceptors>
<action name="Login" class="com.jellythink.practise.LoginAction">
<result name="success">/success.jsp</result>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="InterceptDemo" />
</action>
</package>
</struts>
特别需要注意的是,如果为Action指定了一个拦截器,则系统默认的拦截器栈将会失去作用,为了能够继续使用默认拦截器,所以上面配置文件中显式地应用了默认拦截器。
拦截方法的拦截器
在默认情况下,如果我们为某个Action定义了拦截器,则这个拦截器会拦截该Action内的所有方法。但是在有些情况下,我们不想拦截所有的方法,只需要拦截指定的方法,此时就需要使用Struts2拦截器的方法过滤特性了。
为了实现方法过滤的特性,Struts2提供了一个MethodFilterInterceptor
类,该类是AbstractInterceptor
的子类。通过继承MethodFilterInterceptor
类,我们就可以让拦截器支持方法过滤特性。
下面通过代码来说明问题。
public class MyFilterInterceptor extends MethodFilterInterceptor
{
private String name; // 拦截器名字
public void setName(String name)
{
this.name = name;
}
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();
System.out.println(name + "拦截器的动作----------------" + "执行完该Action的事件为" + (end - start) + "毫秒");
return result;
}
}
通过代码可以看到,支持方法过滤特性的拦截器和普通的拦截器几乎是一致的,只是简单的拦截器需要重写intercept
方法,而支持方法过滤特性的拦截器需要实现doIntercept
方法,仅此而已。但是,这又如何实现方法过滤呢?
在父类MethodFilterInterceptor
中,还额外增加了以下两个方法:
public void setExcludeMethods(String excludeMethods)
:排除需要过滤的方法——设置方法“黑名单”,所有在excludeMethods
字符串中列出的方法都不会被拦截;public void setIncludeMethods(String includeMethods)
:设置需要过滤的方法——设置方法“白名单”,所有在includeMethods
字符串中列出的方法都会被拦截。
下面通过一个小的DEMO来说说如何设置“黑名单”和“白名单”,以及如何使用这个方法过滤的特性。
前台JSP页面:
<body>
<form action="Login!login" method="post">
用户名:<input type="text" name="strName" />
密码:<input type="password" name="strPassword"/>
<input type="submit" value="提交"/>
</form>
<form action="Login!execute" method="post">
用户名:<input type="text" name="strName" />
密码:<input type="password" name="strPassword"/>
<input type="submit" value="提交"/>
</form>
</body>
Struts.xml配置:
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<package name="InterceptDemo" extends="struts-default">
<interceptors>
<interceptor name="InterceptDemo" class="com.jellythink.practise.MyFilterInterceptor">
<param name="name">方法过滤拦截器</param>
</interceptor>
</interceptors>
<action name="Login" class="com.jellythink.practise.LoginAction">
<result name="success">/success.jsp</result>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="InterceptDemo">
<param name="excludeMethods">execute</param>
</interceptor-ref>
</action>
</package>
</struts>
对于execute
方法,由于加入了黑名单,所以不会被拦截,执行时就不会被拦截;而对于login
方法,则会被拦截,console会打印这样的内容:
方法过滤拦截器拦截器的动作----------------开始执行登陆Action的时间为:Fri May 20 20:29:13 CST 2016
执行login方法
方法过滤拦截器拦截器的动作----------------执行完登陆Action的时间为:Fri May 20 20:29:13 CST 2016
方法过滤拦截器拦截器的动作----------------执行完该Action的事件为16毫秒
但是在使用方法过滤特性的拦截器时,需要注意以下两点:
- 如果需要同时指定多个方法不被该拦截器拦截,则多个方法之间以英文逗号(,)隔开,例如:
<interceptor-ref name="InterceptDemo"> <param name="excludeMethods">execute,login</param> </interceptor-ref>
- 如果
excludeMethods
和includeMethods
参数同时指定了一个方法名,则拦截器会拦截该方法。
总结
这篇文章对Struts2框架中的重点内容——拦截器,进行了比较全面的总结,虽然在实际开发中,我们很少有机会去自定义一个拦截器,但是了解和掌握拦截器是深入学习Struts2框架的基础,希望我的这篇文章对你有帮助。
果冻想,认真玩技术的地方。
2016年5月20日 于呼和浩特。