1. 什么是拦截器
拦截器,用于在某个方法或字段被访问之前进行拦截,然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。拦截器是动态拦截Action调用的对象,它提供了一种机制使开发者可以在一个Action执行的前后执行自己编写的代码,也可以在一个Action执行前阻止其执行,同时也是提供了一种可以提取Action中可重用的部分的方式。
拦截器链(Interceptor Chain,在Struts2中称为拦截器栈Interceptor Stack),拦截器链就是将拦截器按一定的顺序联结成一条链,在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
大部分时候,拦截器方法都是通过代理的方式来调用的。Struts2的拦截器实现相对简单,当请求到达Struts2的前端控制器时,Struts2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
总结:Struts2拦截器是可插拔的,拦截器是AOP的一种实现。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链,在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
2. Struts2的执行流程
3. 自定义拦截器
在程序开发过程中,如果需要开发自己的拦截器类,就需要直接或间接的实现com.opensymphony.xwork2.interceptor.Interceptor接口。该接口中定义的代码如下:
public interface Interceptor extends Serializable {
void init();
void destroy();
String intercept(ActionInvocation invocation) throws Exception;
}
该接口提供了三个方法:
- void init():该方法在拦截器被创建后会立即被调用,它在拦截器的生命周期内只被调用一次,可以在该方法中对相关资源进行必要的初始化。
- void destroy():该方法与init方法相对应,在拦截器实例被销毁之前,将调用该方法来释放和拦截器相关的资源,它在拦截器的生命周期内,也只被调用一次。
- String intercept(ActionInvocation invocation) throws Exception:该方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求,该方法就会被调用一次。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该参数的invoke()方法,将控制权转给下一个拦截器或者转给Action的execute()方法。
除了实现Interceptor接口可以自定义拦截器外,更常用的一种方式是继承抽象拦截器类AbstractIntercepter。该类实现了Interceptor接口,并且提供了init()方法和destroy()方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法。AbstractInterceptor类已经实现了Interceptor接口的所有方法,一般情况下,只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。只有当自定义的拦截器需要打开系统资源时,才需要覆盖AbstractInterceptor类的init()方法和destroy()方法。与实现Interceptor接口相比,继承AbstractInterceptor类的方法更为简单。
4. 拦截器的配置
-
拦截器
要想让拦截器起作用,首先要对它进行配置。拦截器的配置是在struts.xml文件中完成的,它通常以<interceptor>标签开头,以</interceptor>标签结束。定义拦截器的语法格式如下:<interceptor name="interceptorName" class="interceptorClass"> <param name="paramName">paramValue</param> </interceptor>
上述语法格式中,name属性用来指定拦截器的名称,class属性用于指定拦截器的实现类。有时,在定义拦截器时需要传入参数,这时需要使用<param>标签,其中name属性用来指定参数的名称,paramValue表示参数的值。
-
拦截器栈
在实际开发中,经常需要在Action执行前同时执行多个拦截动作,如:用户登录检查、登录日志记录以及权限检查等。这时,可以把多个拦截器组成一个拦截器栈,在使用时,可以将栈内的多个拦截器当成一个整体来引用。当拦截器栈被附加到一个Action上时,在执行Action之前必须先执行拦截器栈中的每一个拦截器。定义拦截器栈使用<interceptors>元素和<interceptor-stack>子元素,当配置多个拦截器时,需要使用<interceptor-ref>元素来指定多个拦截器,配置语法如下:
<interceptors> <interceptor-stack name="interceptorstackName"> <interceptor-ref name="interceptorName1"/> <interceptor-ref name="interceptorName2"/> </interceptor-stack> </interceptors>
在上述语法中,interceptorStackName值表示配置的拦截器栈的名称,interceptorName值表示拦截器的名称。除此之外,在一个拦截器栈中还可以包含另一个拦截器栈,示例代码如下:
<package name="default" namespace="/" extends="struts-default"> <interceptors> <!-- 声明拦截器 --> <interceptor name="interceptorl" class="interceptorClass"/> <interceptor name="interceptor2" class="interceptorClass"/> <!-- 定义一个拦截器栈 mystack,该拦截器栈中包含两个拦截器和一个拦截器栈 --> <interceptor-stack name="mystack"> <interceptor-ref name="interceptorl"/> <interceptor-ref name="interceptor2"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> </package>
在上述代码中,定义的拦截器栈是myStack,在myStack栈中,除了引用了两个自定义的拦截器interceptor1和interceptor2外,还引用了一个内置拦截器栈defaultStack(这个拦截器是struts2的内置拦截器栈,是必须要引入的)。
5. 自定义拦截器案例
public class LoginInterceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
//获得 session
Map<String, Object> session = ActionContext.getContext().getSession();
//获得登陆标识
Object object = session.get("user");
//判断登陆标识是否存在
if(object == null){
//不存在则认为没登录,重定向到登录页面
return "toLogin";
}else{
//存在则认为已经登陆,放行
return invocation.invoke();
}
}
}
<struts>
<package name="demo1" namespace="/" extends="struts-default" >
<interceptors>
<!-- 注册拦截器 -->
<interceptor name="loginInterceptor" class="cn.joker.interceptor.LoginInterceptor"></interceptor>
<!-- 注册拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="loginInterceptor">
<!-- 指定哪些方法不拦截 -->
<param name="excludeMethods">login</param>
<!-- 指定哪些方法需要拦截 -->
<param name="includeMethods">find,add,update,delete</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 指定包中的默认拦截器栈 -->
<default-interceptor-ref name="myStack"></default-interceptor-ref>
<action name="CustomerAction_*" class="cn.joker.action.CustomerAction" method="{1}" >
<result name="toLogin" >/login.jsp</result>
<result name="success" >/hello.jsp</result>
</action>
</package>
</struts>