拦截器体系是struts2的一个重要的组成部分,正式大量的内建拦截器完成了该框架的大部分操作。比如:params拦截器将HTTP请求中的参数解析出来,设置成Action的属性;servlet-config拦截器直接将HTTP请求中的HttpServletRequest实例和HttpServletResponse实例传给Action;fileUpload拦截器则负责解析请求参数中的文件域,并将一个文件域设置成Action的三个属性等等。对于struts2的拦截器体系而言,当我们需要使用某个拦截器时,只需要再配置文件中应用该拦截器即可。如果不需要使用该拦截器,只需要取消在配置文件中应用该拦截器。
拦截器时对调用方法的改进,实际上,当称某个实例是一个拦截器时,这是就其行为上而言,但从代码角度来看,拦截器就是一个类,这个类也包含方法,只是这个方法是个特殊方法,它会在目标方法调用之前自动执行。
拦截器与AOP的关系
代理工厂负责根据目标对象和对应的拦截器生成新的代理对象,代理对象的方法是目标方法和拦截器器方的组合,通过这种方式实现了在目标方法之前或之后,自动调用拦截器方法大的目的。代理对象也称为AOP代理,这是由系统动态生成的一个对象,该对象将代替目标对象来使用。
拦截器域AOP是密切关系的,在AOP的编程方式中,有三个重要概念:
目标对象:包含被拦截方的原始对象。
被插入的处理方:定义在拦截器中,会在被拦截方法之前,之后自动执行的方法。
代理对象:以目标对象为蓝本,由系统创建的新对象。
1配置拦截器
拦截器一般写在<package>中,在struts.xml文件中定义拦截器只需要为拦截器类指定一个拦截器名,就完成拦截器定义。定义拦截器使用<interceptor../>元素来定义,定义拦截器最简单的格式如下:
<interceptors>
<interceptor name="拦截器" class="拦截器实现类"/>
<!--
大部分时候,只需要通过上面得格式来完成拦截器的配置,如果还需要在配置拦截器时传入拦截器参数,则需要在<intercepter../>元素中使用<param../>子元素,如:
-->
<interceptor name="拦截器名" class="拦截器实现类"/>
<param name="参数名">参数值</param>
</interceptor>
<interceptors>
例如:
<interceptors>
<interceptor name="firstInterceptor" class="action.firstInterceptor">
<param name="lanjie">my first interceptor</param>
</interceptor>
</interceptors>
此外,还可以包含多个拦截器连在一起组成拦截器栈,定义拦截器栈使用<intercepter-stack../>元素,拦截器栈由多个<intercepter-ref../>元素指定的拦截器组成。拦截器栈和拦截器是统一的,它们包含的方法都会在Action的execute方法执行之前自动执行。拦截器栈与拦截器的功能几乎完全相同,因此可以在拦截器栈中包含拦截器栈。
<interceptor-ref name="defaultStack"/>是系统默认的拦截器栈,如果我们在<action../>中添加了自己的拦截器后还需要添加:<interceptor-ref name="defaultStack"/>,因为它是系统默认的拦截器栈。它可以将参数的值赋给Action相应的属性。
在<action..>...</action>中使用拦截器时通过<interceptor-ref.../>元素来引用在<package.../>中定义的拦截器。
默认拦截器
当配置一个包时,可以为其指定默认拦截器,一旦为某个包指定了默认了默认的拦截器,如果该包中的Action没有显式指定拦截器,则默认的拦截器将会起作用。但是一旦我们为该包中的Action显式指定了某个拦截器,则默认的拦截器不会起作用,如果该Action需要使用该默认拦截器,则必须手动配置该拦截器的引用。
配置默认呢拦截器使用<default-interceptor-ref../>元素,该元素作为<package.../>元素的子元素使用,为该包下的所有Action配置了默认拦截器。配置<default-interceptor-ref../>元素时,需要制定一个name属性,该name属性值是一个已经存在拦截器的名或拦截器栈名,表明将该拦截器配置成该包的默认拦截器。注意每个<package../>元素只能有一个<default-interceptor-ref../>子元素,即每个包只能指定一个默认拦截器。
与Action中使用拦截器一样,也可以在配置默认拦截器时为该拦截器指定参数,因此,指定默认拦截器的<default-interceptor-ref../>元素
同样支持<param../>子元素:该元素用于定义
在Action中定义的包都是struts-default包的子包,当我们定义的包继承struts-default包时,也继承了它的默认拦截器栈:defaultStack,这就意味着,如果我们不为Action指定人很拦截器引用,则defaultStack拦截器栈拦截Action。
<interceptors ../>:该元素用于定义拦截器,所有的拦截器和拦截器栈都在该元素下定义,而不能直接放在<package..>中。该元素包含<interceptor../>和<interceptor-stack/>子元素,分别用于定义拦截器和拦截器栈。
<interceptor../>元素,该元素用于定义拦截器,定义拦截器只需要指定两个属性:name和class
<interceptor-stack../>元素,该元素用于定义拦截器引用,该元素中包含多个<interceptor-ref../>元素。
<interceptor-ref../>元素,该元素引用一个拦截器或拦截器栈,该元素只需要制定一个name属性,该name属性值为一个已经定义的拦截器和拦截器栈。
<param../>该元素用于为拦截器指定参数,可以作为<interceptor.../>和<interceptor-ref../>元素的子元素使用。注意使用<param.../>元素向拦截器传递参数与<action.../>元素中使用<param../>一样。可以参考<action.../>中的<param.../>
2使用自定义拦截器
如果用户要开发自己的拦截器类,应该实现com.opensymphony.xwork2.interceptor.Interceptor接口,该接口的类定义代码如下:
public interface Interceptor extends Serializable{
//销毁该拦截器之前的回调方法
void destroy();
//初始化拦截器的回调方法
void init();
//拦截器实现拦截的逻辑方法
String intercept(ActionInvocation invocation)throws Exception;
}
init(),在该拦截器被初始化之后,在该拦截器执行拦截之前,系统将回调该方法。对于每个拦截器而言,该init()方法只执行一次。
destroy(),在拦截器实例被销毁之前,系统将回调该拦截器的destroy方法。
intercept(ActionInvocation invocation),该方法是用户需要实现的拦截动作,就像Action的execute方法一样,intercept方法会返回一个字符串作为逻辑视图,如果该方法直接返回一个字符串,系统将会跳转到该逻辑视图对应的实际视图资源,不会调用被拦截的Action。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过调用该参数的invoke方法,将控制权转给下一拦截器,或转给Action的execute方法。
struts2还提供了一个AbstractInterceptor类,该类提供了一个init和destroy方法的空实现,我们也可以继承AbstractInterceptor类来实现自定义拦截器。
下面是一个简单的拦截器类:
package action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class firstInterceptor extends AbstractInterceptor {
//定义属性lanjie对应于<param name="lanjie">my first interceptor</param>
private String lanjie;
public String getLanjie() {
return lanjie;
}
public void setLanjie(String lanjie) {
this.lanjie = lanjie;
}
//重写intercept方法
public String intercept(ActionInvocation invocation) throws Exception{
System.out.println("*****************start interceptor"+lanjie);
//将控制权转交给下一个拦截器,如果没有拦截器了就执行Action中方法
String result=invocation.invoke();
System.out.println("*****************"+result);
return result;
}
}
3方法过滤
默认情况下,如果我们为某个Action定义了拦截器,则这个拦截器会拦截Action内的所有方法。可能我们只需要拦截某些方法,就需要实现方法过滤,struts2提供了一个MethodFilterInterceptor类,该类是AbstractInterceptor类的子类,如果用户需要自己实现的拦截支持方法过滤特性,则应该继承MethodFilterInterceptor。
实际上,实现方法过滤的拦截器与实现普通拦截器并没有太大区别,只需要注意两个地方:实现方法过滤的拦截器需要继承MethodFilterInterceptor抽象类和重写doIntercept方法定义对Action的拦截逻辑。在MethodFilterInterceptor方法,额外增加了如下两个方法:
public void setExcludeMethods(String excludeMethods),排除需要过滤的方法,所有在excludeMethods字符串中列出的方法都不会被拦截。
public void setIncludeMethod(String includeMethods),设置需要过滤的方法,所有在includeMethods字符串中列出的方法都会被拦截。
如果一个方法同时在excludeMethods和includeMethods中列出,则该方法会被拦截。
因为MethodFilterInterceptor类包含了上面的两个方法,所以它的子类也包含这两个方法,便可以直接在配置文件中指定需要被拦截或不需要被拦截的方法,方法过滤示例应用的配置片段:
<interceptor-ref name="myfilter">
<!--重新指定name属性的属性值-->
<param name="name">改名后的方法过滤拦截器</param>
<!--指定execute方法不需要拦截-->
<param name="excludeMethods">execute,nofilter</param>
<!--指定test方法需要拦截-->
<param name="includeMethods">test</param>
</interceptor-ref>
如果需要同时指定多个方法不被该拦截器拦截,则多个方法之间以英文逗号(,)隔开。
方法过滤类的实例:
package action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class firstInterceptor extends MethodFilterInterceptor {
private String lanjie;
public String getLanjie() {
return lanjie;
}
public void setLanjie(String lanjie) {
this.lanjie = lanjie;
}
//重写dointercept方法
public String doIntercept(ActionInvocation invocation) throws Exception{
System.out.println("*****************start interceptor"+lanjie);
String result=invocation.invoke();
System.out.println("*****************result");
return result;
}
}
拦截器的执行顺序
在Action中的方法执行之前的拦截动作,将按照拦截器定义的先后顺序执行,在Action中的方法执行之后的拦截动作,则执行顺序相反。
4拦截结果的监听器
在前面的拦截器中,我盟将在execute方法执行之前,执行之后的动作都定义在拦截器的intercept方法或doIntercept方法中,为了精确定义在execute方法执行结束后,再处理Result执行的动作,struts2提供了用于拦截结果的监听器,这个监听器是通过手动注册在拦截内部的。
实现拦截结果的监听器必须实现PreResultListener接口,例如:
package action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
public class firstInterceptor extends MethodFilterInterceptor {
private String lanjie;
public String getLanjie() {
return lanjie;
}
public void setLanjie(String lanjie) {
this.lanjie = lanjie;
}
public String doIntercept(ActionInvocation invocation) throws Exception{
//将拦截结果的监听器手动注册给某个拦截器
invocation.addPreResultListener(new myresultListener());
System.out.println("*****************start interceptor"+lanjie);
String result=invocation.invoke();
System.out.println("*****************"+lanjie+" "+result);
return result;
}
//定义在处理Result之前的行为
}
//在firstInterceptor.java这个类定义了两个类firstInterceptor和myresultListener
class myresultListener implements PreResultListener{
public void beforeResult(ActionInvocation invocation,String resultCode){
System.out.println("返回的逻辑类型:"+resultCode);
}
}
拦截结果的监听器是实现了PreResultListener接口的监听器。该接口有一个beforeResult方法,
beforeResult方法中的参数resultCode,这个参数就是被拦截的Action的execute方法的返回值,虽然beforeResult方法也获得ActionInvocation类型的参数,但是这时Action的execute方法已经执行结束了。通过.addPreResultListener()方法注册拦截结果的监听器。该监听器中的beforeResult方法可定会在系统处理Result之前执行。
注意:不要再beforeResult方法中可以获得ActionInvocation实例,但不要再次调用invoke()方法,如果再次调用invoke()方法,将会再次执行Action处理,将陷入一个死循环。
5覆盖拦截器中特定拦截器的参数
我们在Action配置中引用拦截器并通过<param.../>元素来修改拦截器属性值。但是这种方式有个缺点,加入我们引用的是一个拦截器栈,那么要修改这个拦截器栈中的拦截器的属性,则必须在该Action内重新引用这个拦截器。struts2提供了另一个更加简单的修改方式:当引用拦截器栈时直接修改该栈内拦截器的属性值,采用如下方式的配置片段:
<!--引用拦截器栈my-stack-->
<interceptor-ref name="my-statck">
<param name="second.name">更改拦截器second的属性name的值</param>
</interceptor-ref>
可以看出:如果需要再引用拦截器栈时直接覆盖栈内某个拦截器的属性值,则在指定需要被覆盖的属性时,不能只指定该属性的属性名,必须加上该属性属于的拦截器。即采用如下形式:
<param name="拦截器名.属性名">新的属性值</param>
在struts-default.xml文件时struts2默认的配置文件,这个配置文件会自动加载,struts2大部分的内建拦截器都配置在该文件中。如果我们定义的package继承了struts2的默认struts-default包,则可以自由使用struts2内建的拦截器。
********************
如果给一个action配置了多个拦截器,如果有几个拦截器同名,则这些拦截器都会被执行。invoke方法将控制权移交给下一个拦截器,直到所有的拦截器都执行完毕,则将控制权移交给Action。
********************
如果我们给action定义了拦截器,需把这个拦截器放在默认拦截器<interceptor-ref name="defaultStack"/>前面。另外在默认拦截器栈执行之前Action的属性是没有值的。
**************
ActionInvocation实例的getAction()获得被拦截的Action类的对象,返回的是一个Object对象。例如:
checkLogin fc=(checkLogin)invocation.getAction();
ActionInvocation实例的getInvocationContext()方法获得ActionContext对象。例如:
ActionContext ctx=invocation.getInvocationContext();ctx.getSession();获得session对象。