拦截器(Interceptor)是Struts2的核心组成部分。很多功能(Feature)是构建在拦截器基础上的,例如文件的上传和下载、国际化、转换器和数据校验等,Struts2利用内建的拦截器,完成了框架内的大部分操作。
拦截器动态拦截Action调用的对象,它提供了一种机制,使开发者可以定义一个特定的功能模块,这个模块可以在Action执行之前或者执行之后运行,也可以在一个Action执行之前阻止Action执行。同时也提供了一种可以提取Action中可重用的部分的方式。
在Struts2架构中,可以使用配置文件,灵活配置定义拦截器。当需要一个拦截器的时候,只要在配置文件中设置即可;如果不需要时,可以取消该拦截器,这样就可以实现拦截器同Action类的松耦合,提高系统扩展性。
一、 Struts2拦截器原理
Struts2架构的Action被一个或者多个拦截器(拦截器栈)所包围,所有的用户请求都会被拦截器所拦截,然后交给Action处理,处理结果以逻辑视图方式返回给用户。 这个调用执行流程是由Struts2的配置文件来实现的。 当用户请求到达Struts2的ServletDispatcher时,Struts2会查找配置文件,并根据其配置实例化相对的拦截对象,然后串成一个列表(List),最后一个一个地调用列表中的拦截器。
二、 定义拦截器
Struts2框架的拦截器处理机制采用了AOP(面向方面编程)设计思想。使得拦截器(拦截器组)可以类似“热插拔”的方式来组合,配合业务控制器Action来完成用户请求的处理。 这种“热插拔”实际上是通过Struts2框架的配置文件来实现的,配置文件中可以定义拦截器和拦截器栈,并可以在Action中定义所使用的拦截器或者拦截器栈。开发者如果要改变拦截器的执行顺序或者数量,只需要修改配置文件即可。
在struts.xml配置文件中,配置拦截器如下:
<interceptors>
<!—定义拦截器-->
<interceptor class="intercept.FilterInterceptor"
name="filterIntercept"></interceptor>
<interceptor class="interceptOrder.InterceptorOrder"
name="InterceptorOrder">
<!—定义拦截器参数-->
<param name="">value</param>
<param name="">value</param>
</interceptor>
<interceptor name="checkIntercept"
class="authorityIntercept.CheckInterceptor">
</interceptor>
<!—定义拦截器栈-->
<interceptor-stack name="myDefaultStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="checkIntercept"></interceptor-ref>
</interceptor-stack>
</interceptors>
三、 使用拦截器
1、在Struts2框架中定义了拦截器或者拦截器栈,就可以在配置文件中使用该拦截器或者拦截器栈来拦截Action。指定拦截器或者拦截器栈,会在Action的execute()方法执行之前被执行。 Action中配置拦截器如下:
<action name="FilterAction" class="intercept.FilterAction">
<result name="success">/intercept/MethodFilter.jsp</result>
<!—显式引用默认拦截器-->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!—自定义拦截器栈-->
<interceptor-ref name="myDefaultStack">
</interceptorref>
<interceptor-ref name="filterIntercept">
<!—在使用拦截器时指定拦截器参数-->
<param name="includeMethods">method1</param>
<param name="excludeMethods">method2</param>
<param name="name">方法过滤拦截器</param>
</interceptor-ref>
</action>
注意:Struts2框架的默认拦截器defaultStack包含了很多重要的功能,在Action定
义自己的拦截器的同时,一定要注意需要同时显式的引用系统默认拦截器
defaultStack。
2、 配置默认拦截器
为了避免每个Action配置相同的拦截器,使用默认拦截器,则包内所有Action都会自动使用默认拦截器,避免了配置代码的重复使用。
配置默认拦截器使用<default-interceptor-ref…/>元素,该元素为包<package…/>的一个子元素,如果在包配置中定义了一个默认拦截器,那么该拦截器对包内所有的Action都是有效的,Action中显式定义拦截器情况下除外。
<default-interceptor-ref…/>元素需要指定一个name属性,该属性指定默认拦截器的名称。该属性必须是一个已经存在的拦截器名称,即前面已经定义好了的。
<interceptors>
<interceptor name="checkInterceptor" class="bbs.checkInterceptor">
</interceptor>
<interceptor-stack name="bbsStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="checkInterceptor" />
<interceptor-ref name="jsfStack" />
</interceptor-stack>
</interceptors>
<!—指定包内默认拦截器-->
<default-interceptor-ref name="bbsStack"></default-interceptor-ref>
注意:在指定包内默认拦截器时,只能使用一个<default-interceptor-ref…/>元素,即
每个包内只能定义一个默认的拦截器。 如果需要将多个拦截器都设置为一个包的默
认拦截器,最好的方式是将这些拦截器组成一个拦截器栈,再将拦截器栈设置为包
的默认拦截器。
3、 自定义拦截器实现类
1、 Struts2框架为开发者自定义拦截器实现类提供了一个Interceptor接口,用户可
以实现该接口来开发自定义拦截器实现类。Interceptor接口中定义了三个方法:
init()、destroy()和String interceptor(ActionInvocation invocation)。
2、 Struts2框架除了提供Interceptor接口用于开发自定义拦截器实现类外,还提供
了一个com.opensymphony.xwork.interceptor.AbstractInterceptor 类,
开发者只要继承该类,就可以用简单的方式来实现自己的拦截器实现类。该类实
现了Interceptor接口。 该类同样定义了上面的三个方法,如果在用户自定义的
拦截器实现类的初始化时,不需要加载一些特殊的系统资源,可以不用实现init()
和destroy()方法。只需要重写interceptor(ActionInvocation invocation)方
法即可。
四、 拦截器深度剖析
1、 拦截器的方法过滤
业务控制器Action中可以使用动态方法来处理用户请求,这样Struts2框架的Action更
加灵活。 在Action中使用拦截器,默认情况下会拦截Action实现类中的所有方法。但
是某些情况下,开发者可能只需要拦截Action中的一个或者多个方法,有时候也希望拦
截器不拦截某些Action方法。 这时,让拦截器有选择的拦截Action中的某个方法,就
需要使用拦截器的方法过滤。
Struts2框架提供了一个MethodFilterInterceptor类,开发者自定义的拦截器只要继承
该类,就可以使用拦截器的方法过滤功能,来拦截Action中特定的方法。
MethodFilterInterceptor类为
com.opensymphony.xwork.interceptor.AbstractInterceptor拦截器类的子类,实现
了Interceptor和Serializable接口。该类有两个重要的参数:
excludeMethods:该参数指定拦截器拒绝拦截的方法列表。多个方法值中间用逗号分开。
includeMethods: 该参数指定拦截器需要拦截的方法列表。多个方法值中间用逗号分开。
下面给出该类的主要方法:
protected abstract String doInterceptor(ActionInvocation invocation) :继承该类的子类必须重写该方法,并实现拦截器逻辑。
String interceptor(ActionInvocation invocation):继承自AbstractInterceptor类,该方法不需要强制重写。
void setExcludeMethods(String excludeMethods):设置黑名单,该方法参数为一个字符
串,即对应的Action方法名称。
void setIncludeMethods(String includeMethods):设置拦截器的白名单。
Set getExcludeMethodsSet():获得拦截器的黑名单。
Set getIncludeMethodsSet(): 获得拦截器的白名单。
注意: 开发者使用MethodFilterInterceptor类的子类来实现拦截器的方法过滤,只需要
重写doInterceptor(ActionInvocation invocation)方法即可。
下面为实现方法过滤拦截的配置文件: filterInterceptor过滤器继承了MethodFilterInterceptor类。
<interceptor-ref name="filterIntercept">
<!—使用方法过滤,设置白名单或黑名单-->
<param name="includeMethods">method1</param>
<param name="excludeMethods">method2</param>
<param name="name">方法过滤拦截器</param>
</interceptor-ref>
2、 拦截器的执行顺序
配置在前面的拦截器,会在被拦截方法执行之前执行拦截动作,拦截器的拦截动作是按照配置文件拦截器的引用顺序来执行的。
3、 拦截结果监听器
开发者如果需要在Action执行之后,拦截器的interceptor(ActionInvocation arg0)返回result之前进行一些业务处理操作。例如Action执行execute()后,返回一个“success”字符串,拦截器对该返回结果进行处理,修改某些业务数据后,再返回“success”逻辑视图。
1、 实现上面的要求,可以在拦截器的invoke()方法之后加入处理代码。例如:
public String intercept(ActionInvocation arg0) throws Exception {
//注册一个监听器
arg0.addPreResultListener(new MyListener());
System.out.println(name+"拦截器信息:启动拦截器,拦截Action时
间:"+new Date());
String result=arg0.invoke();
//根据处理结果处理其他业务
if(result.equals("success")){
dosomething();
}
System.out.println(name+"拦截器信息:Action执行完毕时间:"+new
Date());
return result;
}
2、 上面代码会导致拦截器中加入了大量的业务逻辑判断和处理代码,从而降低了系统代码的复用性。 为了处理Action执行处理方法之后和拦截器返回result之间节点的业务,Struts2框架提供了一个PreResultListener接口。实现该接口就可以监听Action特定方法执行之后的结果,并做进一步的预后处理。
PreResultListener接口中只有一个方法,如下所示。该方法中有两个参数,其中resultcode就是Action执行之后返回的结果。
void beforeResult(ActionInvocation invocation, String resultcode)
1. 开发者可以开发自己的监听器类,该类必须实现PreResultListener接口,并重写void beforeResult(ActionInvocation invocation, String resultcode)方法。例如下面的代码:
public class MyListener implements PreResultListener {
public void beforeResult(ActionInvocation arg0, String arg1) {
// TODO Auto-generated method stub
System.out.println("监听器监听执行结果..."+arg1);
}
}
2. 为了使用监听器,需要在拦截器中注册监听器。下面的示例中在拦截器中加入了监听器:
public String intercept(ActionInvocation arg0) throws Exception {
//注册一个监听器
arg0.addPreResultListener(new MyListener());
System.out.println(name+"拦截器信息:启动拦截器,拦截Action时间:"+new Date());
String result=arg0.invoke();
System.out.println(name+"拦截器信息:Action执行完毕时间:"+new Date());
return result;
}