Filter、Interceptor都是AOP思想的体现。
Filter(过滤器)会拦截所有的请求,对html、jsp、Servlet等资源的请求都会被拦截。
Interceptor(拦截器)只拦截对Action的请求,且可以实现细粒化拦截,可以只拦截Action中的部分方法。
Filter、Interceptor的执行顺序都是:去的时候依次执行1,2,3,回来的时候就依次执行3,2,1。
因为是在1里面放行,调用2,在2里面放行,调用3,3里面放行调用Action|Servlet|JSP|html,得到响应对象,
响应对象返还给3,3对响应对象进行处理,返回给2,2对响应对象进行处理,返回给1,1对响应对象进行处理,返回给服务器,服务器返回给浏览器。
相当于递归调用。
注意是先调用JSP,由JSP组成响应,再由拦截器对响应进行进一步处理。
拦截器能拦截请求,也能拦截响应。
拦截器的配置
<struts> <package name="action" namespace="/action" extends="struts-default"> <interceptors> <interceptor name="" class=""></interceptor> <interceptor-stack name=""> <interceptor-ref name=""></interceptor-ref> <interceptor-ref name=""></interceptor-ref> <interceptor-ref name=""></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name=""></default-interceptor-ref> <action name="" class=""> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name=""></interceptor-ref> <result name=""></result> <result name=""></result> </action> </package> </struts>
在<interceptors>中注册拦截器、拦截器栈,注册之后就可以通过name来引用拦截器、拦截器栈。可以用拦截器栈来组合一组拦截器。
<default-interceptor-ref name=""></default-interceptor-ref>配置默认引用的拦截器、拦截器栈,此配置会覆盖struts-default.xml中的默认拦截器配置。如果我们配置了拦截器、拦截器栈的引用,就不会再自动调用默认的;如果我们没有配置拦截器、拦截器栈的引用,会自动调用默认的。
<interceptors>、<default-interceptor-ref>均只能作为<package>的子元素来配置。
在<action>中使用<interceptor-ref>来引用拦截器、拦截器栈,可同时引用多个,默认拦截器栈中有很多通用处理,一般都要引用。
<interceptor-ref>只能作为<action>的子元素来配置。
去的时候,按照引用的先后顺序依次调用拦截器,回来的时候,调用顺序相反。
struts-default.xml中的默认拦截器配置:
<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="datetime"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="debugging"/> </interceptor-stack>
<default-interceptor-ref name="defaultStack"/>
servletConfig拦截器用于获取原生的Servlet API。
modelDriven用于模型驱动获取参数。
fileUpload用于文件上传。
params用于封装请求参数。
.........
默认的拦截器(栈)有大量的通用操作,一般都要引用。如果不引用默认的拦截器(栈),struts2的很多功能都用不了。
拦截器是Struts2的核心,完成了Struts2的大部分工作,比如解析请求参数、将请求参数赋给Action的成员变量,数据校验,文件上传等。
Struts2的拦截器是可插拔式设计,要使用某个拦截器,在配置文件中引入即可,不使用时在配置文件中删除即可。
自定义拦截器类
Interceptor本质是一个Java类,新建一个Java类即可。有3种方式:
- 实现Interceptor接口 需实现里面所有的抽象方法
- 实现AbstractInterceptor抽象类 这个类是Interceptor的子类,为其它方法提供了空实现,只有核心方法intercept()是抽象的。实现intercept()方法即可。
- 实现MethodFilterInterceptor抽象类 这个类是AbstractInterceptor的(抽象)子类,可以只拦截Action中的部分方法。需要实现doIntercept()方法。
Interceptor接口:
public interface Interceptor extends Serializable { void destroy(); void init(); String intercept(ActionInvocation var1) throws Exception; }
在init()中做初始化,在destroy中清理善后,intercept()是核心方法,用于拦截处理。
使用AbstractInterceptor:
public class MyInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation actionInvocation) throws Exception { //去的时候做一些处理 System.out.println("before"); //放行 String result = actionInvocation.invoke(); //回来的时候做一些处理 System.out.println("after"); return result; } }
需要返回String,如果需要在回来的时候做一些处理,要将return放到最后。
返回的String就是action返回的那个String,如果不想进行后续处理,不调用 actionInvocation.invoke() ,直接返回一个String(逻辑视图名)即可。
如果不调用JSP来显示,可以 return null; 。
使用MethodFilterInterceptor拦截action中的部分方法:
public class MyInterceptor extends MethodFilterInterceptor { @Override protected String doIntercept(ActionInvocation actionInvocation) throws Exception { //去的时候做一些处理 System.out.println("before"); //放行 String result = actionInvocation.invoke(); //回来的时候做一些处理 System.out.println("after"); return result; } }
Action中有多个处理业务的方法:
public class MyAction{ public String add(){ System.out.println("add"); return "ok"; } public String update(){ System.out.println("update"); return "ok"; } public String delete(){ System.out.println("delete"); return "ok"; } }
拦截器配置:
<struts> <package name="action" namespace="/" extends="struts-default"> <interceptors> <interceptor name="myInterceptor" class="interceptor.MyInterceptor"> <param name="includeMethods">add,delete</param> </interceptor> </interceptors>
<action name="MyAction_*" class="action.MyAction" method="{1}"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="myInterceptor"></interceptor-ref> <result name="ok">/index.jsp</result> <allowed-methods>add,update,delete</allowed-methods> </action> </package> </struts>
MethodFilterInterceptor常与Action动态方法调用搭配使用。
注册拦截器时,需指定要拦截的方法|不拦截的方法:
<interceptor name="myInterceptor" class="interceptor.MyInterceptor">
<param name="includeMethods">add,delete</param>
<param name="excludeMethods">update</param>
</interceptor>
includeMethods指定此拦截器要拦截的方法,excludeMehtods指定不拦截的方法,这2个参数不能一起使用。指定了其中一个,剩下的方法都是另一个的。
要调用的方法是includeMethods中的方法时,才会使用此拦截器进行拦截,否则不使用此拦截器进行拦截。
此例中,访问MyAction_add、MyAcion_delete时,均会调用此拦截器;访问MyAction_update时,不会调用此拦截器。
处理机制:请求传递给此拦截器,此拦截器检查要调用的方法是否是includeMethods中的方法,是就处理,不是就直接放行。
Interceptor的生命周期
Interceptor在部署|启动WebApp时就创建实例,调用init()进行初始化。
从Tomcat中移除当前WebApp时,Interceptor实例随着WebApp的销毁而销毁,会自动调用destroy()。
Interceptor是单例的,一个Interceptor在Tomcat中只有一个实例。
在Interceptor的生命周期中,init()、destroy()均只调用1次。每次拦截请求都会调用核心拦截方法。
Action是多例的,每次处理请求都会创建一个新的Action实例。
处理完业务,调用JSP之后,此Action的生命周期才结束。
注意是执行要调用的JSP之后,Action实例才销毁。就是说在执行JSP时,此Action的ValueStack还在,仍可以从中取出数据。
使用Interceptor实现权限校验
用户登录,登录成功后可执行相关操作,直接执行用户操作,不再检查用户权限、信息,这是不行的。
比如用户可以直接在地址栏输入URL,转到操作页面进行操作,跳过用户登录。
可以使用拦截器拦截用户请求,检查用户权限:
从session中获取用户信息、权限,如果满足条件,就放行,如果未登录、权限不够,就转发到对应的登录页面、提示页面。
在用户登录成功时,将用户信息、权限放到session中。
可使用MethodFilterInterceptor只拦截Action中需要检查权限的某些方法。