拦截器(interceptor)是Struts2最强大的特性之一,拦截器可以让你在Action和result被执行之前或之后进行一些处理。同时,拦截器也可以让你将通用的代码模块化并作为可重用的类。Struts2中的很多特性都是由拦截器来完成的。例如params拦截器将HTTP请求中的参数解析出来,并设置为Action的属性。servlet-config拦截器直接将HTTP请求中的HttpServletRequest对象化HttpServletResponse对象传给Action。
Struts2中内置类许多的拦截器,它们提供了许多Struts2的核心功能和可选的高级特性。这些内置的拦截器在struts-default.xml中配置。只有配置了拦截器,拦截器才可以正常的工作和运行。在struts-default.xml中拦截器的配置片段为:
<package name="struts-default" abstract="true"> ① <interceptors> <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> ② <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> //…其他拦截器配置 <interceptor-stack name="defaultStack"> ③ <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> //…其他拦截器的引用 </interceptor-stack> </interceptors> <default-interceptor-ref name="defaultStack"/> ④ </package>① package将属性abstract设置为true,代表此package为一个抽象的package。抽象package和非抽象package的区别在于抽象的package中不能配置action。
② name属性指定拦截器的名字,class属性指定拦截器的完全限定名。
③ 多个拦截器可以组成拦截器栈。name属性为拦截器栈的名字。
④ 指定当前package的默认拦截器(栈)。当前指定的默认拦截器栈为defaultStack,该拦截器栈是Struts2运行的一个基本拦截器栈,一般我们不用在自己配置它,因为在大多数情况下,我们自定义的package是继承自struts-default这个package的。
我们以Struts2内置的timer拦截器为例,来学习如何在我们的应用中添加其他的拦截器。timer拦截器可以统计action执行的时间。我们可以修改package中默认的拦截器,那么将替换掉struts-default中配置的defaultStack拦截器栈,导致Struts2无法正常运行,比如无法获取表单的值等等。那么该如何正确的配置呢?可以在添加新的拦截器的基础上加入defaultStack拦截器栈,这样就可以保证defaultStack拦截器栈的存在。
<package name="myStruts" extends="struts-default"> <interceptors> <interceptor-stack name="myInterceptor"> ① <interceptor-ref name="timer"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="myInterceptor"/> ② <action name="userAction" class="com.kay.action.UserAction"> <result name="success">suc.jsp</result> <result name="input">index.jsp</result> <result name="error">err.jsp</result> </action> </package>① 添加一个自定义的拦截器栈,并在其中包含time拦截器和defaultStack拦截器栈。
② 设置当前的package的默认拦截器栈为自定义的拦截器栈。
修改package的默认拦截器会应用的package中的所有Action中,如果只想给其中一个Action添加拦截器,则可以这样来做:
<package name="myStruts" extends="struts-default"> <interceptors> <interceptor-stack name="myInterceptor"> <interceptor-ref name="timer"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <action name="userAction" class="com.kay.action.UserAction"> <interceptor-ref name="myInterceptor"/> ① <result name="success">suc.jsp</result> <result name="input">index.jsp</result> <result name="error">err.jsp</result> </action> </package>① 给UserAction添加拦截器。
如果要创建自己的拦截器,只需要实现Interceptor接口,该接口中定义了以下三个方法:
void init():
在拦截器初始化之后,在执行拦截之前,系统调用该方法。对于一个拦截器而言,init方法只会被调用一次。
String intercept(ActionInvocation invocation) throws Exception:
该方法是拦截器的拦截方法,返回一个字符串,系统将会跳转到该字符串对应的视图资源。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该对象的invoke方法,将控制权转给下一个拦截器或者转给Action的execute方法。
void destroy():
该方法与init方法对应,在拦截器示例被销毁之前,系统将会调用该方法。
除了Interceptor接口外,Struts2中还提供了一个AbStractInterceptor类,该类提供了一个init和destroy方法的空实现。如果不需要就不用重写这两个方法,可见继承自AbstractInterceptor类可以让我们构建拦截器时变得简单。
下面我们构建自己的拦截器,实现timer拦截器的效果。
public class MyInterceptor extends AbstractInterceptor { public String intercept(ActionInvocation invocation) throws Exception { long startTime = System.currentTimeMillis(); ① String result = invocation.invoke(); ② long endTime = System.currentTimeMillis(); ③ System.out.println("Action执行共需要" + (endTime - startTime) + "毫秒"); return result; } }① 获得Action执行的开始时间。
② 将控制权交给下一个拦截器,如果该拦截器是最后一个拦截器,则调用Action的execute方法。
③ 获得Action执行的结束时间。
在配置文件struts.xml中配置拦截器:
<package name="myStruts" extends="struts-default"> <interceptors> <interceptor name="myTimer" class="com.kay.interceptor.MyInterceptor"></interceptor> ① <interceptor-stack name="myInterceptor"> <interceptor-ref name="myTimer"/> ② <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <action name="userAction" class="com.kay.action.UserAction"> <interceptor-ref name="myInterceptor"/> <result name="success">suc.jsp</result> <result name="input">index.jsp</result> <result name="error">err.jsp</result> </action> </package>① 定义一个新的拦截器,name属性为拦截器的名字,class属性为拦截器的完全限定名。
② 在拦截器栈中加入新的拦截器。
从拦截器的运行原理上和我们以前学习的Servlet中的过滤器是不是很相像呢?其实它们只有一个重要的区别,就是拦截器的工作是不依赖容器的,这会在进行单元测试时变得简单。