Struts 2专栏目录
Struts 2拦截器(Interceptor)
Struts 2组成部分
Struts 2由两部分组成:①XWork 2、②Struts 2
XWork 2是一个命令模式的框架,是Struts 2的基础;它的核心包括Action、Result、拦截器。Struts 2扩展了这些概念的基础实现,用于支持Web应用程序的开发。
为什么使用拦截器?
任何优秀的MVC框架都会提供一些通用的操作,如请求数据的封装、类型转换、数据校验、解析上传的文件、防止表单的多次提交等。
Struts 2将它的核心功能放到拦截器中实现而不是集中在核心控制器中实现,把大部分控制器需要完成的工作按功能分开定义,每个拦截器完成一个功能,而完成这些功能的拦截器可以自由选择、灵活组合,需要哪些只需要在struts.xml指定即可,从而增加了框架的灵活性,有利于系统解耦。
拦截器的方法在Action执行之前或者执行之后自动的执行,从而将通用的操作动态地插入到了可插拔式。需要哪个就“插入”一个拦截器,不需要某个功能就“拔出”一个拦截器。可以任意地组合Action提供的附加功能,而不需要修改Action代码。
如果有一批拦截器经常固定在一起使用,可以将这些执行小粒度功能的拦截器定义成大粒度的拦截器栈(根据不同应用需求而定义的拦截器组合)。
从结构上看,拦截器栈相当于与多个拦截器的组合
从功能上看,拦截器栈也是拦截器,同样可以和其他拦截器(或拦截器栈)一起组成更大粒度的拦截器栈
拦截器的工作原理
Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
拦截器围挠Action和Result的执行而执行
拦截器的实现原理和Servlet Filter的实现原理差不多,以链式执行,对真正要执行的方法(execute())进行拦截
首先执行Action配置拦截器,在Action和Result执行之后,拦截器在一次执行(与先前调用相反的顺序),在此链式的执行过程中,任何一个拦截器都可以直接返回,从而终止余下的拦截器、Action及Result的执行
当ActionInvocation的invoke()方法被调用时,开始执行Action配置的第一个拦截器,拦截器做出相应处理后会再次调用ActionInvocation的invoke()方法,ActionInvocation对象负责跟踪执行过程的状态,并把控制权交给适合的拦截器。ActionInvocation通过调用拦截器的intercept()方法将控制转交给拦截器
拦截器的执行过程可以看作一个递归的过程,后续拦截器继续执行,直到最后一个拦截器,invoke()方法会执行Action
为什么拦截器的执行看作递归过程
框架通过第一次调用ActionInvocation的invoke()方法开始这一过程,ActionInvocation通过调用拦截器的intercept()方法把控制权转交给为Action配置的第一个拦截器。最重要的是intercept()方法把ActionInvocation实例看作参数,在拦截器的处理过程中,它会调用ActionInvocation对象上的invoke()方法来继续调用后续按揭器。
拦截器有三个阶段,有条件的执行周期:
(1)做一些Action执行前的预处理。拦截器可以准备、过滤、改变或操作任何可以访问的数据、包括Action
(2)调用ActionInvocation的invoke()方法将控制权转交给后续的拦截器或者返回结果字符串终止执行。如果拦截器决定请求的处理不应该继续,可以不调用invoke()方法,而是直接返回一个控制字符串。通过这种方式,可以停止后续的执行,并且决定将那个结果呈现给客户端
(3)做一些Action执行后的处理。此时拦截器依然可以访问改变可以访问的对象和数据,只是此时框架已经选择了一个结果呈现给客户端了
拦截器的配置
两个步骤
(1)通过<interceptors/>的子元素<interceptor……/>元素来定义拦截器(package下)
(2)通过<interceptor-ref……/>元素来使用拦截器(action下)
在struts.xml文件中,首先在<package>中配置<interceptors/>的子元素<interceptor……/>来定义拦截器,<interceptor>元素的name属性与class属性是必须填写的
属性 | 描述 |
---|---|
name | 指定拦截器的名字,依然使用interceptor-ref元素指定引用的拦截器 |
class | 指定拦截器的全限定类名 |
然后在<action>元素中使用<interceptor-ref>子元素指定引用的拦截器,如果还需要指定默认拦截器,就需要一并添加到<action>中(default-interceptor-ref)
如果想要把多个拦截器组成一个拦截器栈,就需要在interceptors元素中使用interceptor-stack元素定义拦截器栈
Struts 2内置拦截器
1.Params
提供了框架必不可少的功能,将请求中的数据设置到Action的属性上
2.staticParams
将配置文件中通过元素的子元素设置的参数设置到对应的Action的属性中
3.servletConfig
提供了将一种源于Servlet API的各种对象注入Action当中的简洁方法。Action必须实现相对应的接口,servletConfig拦截器才能将对应的Servlet对象注入Action中
接口可以由Action实现,用来取得Servlet API的不同对象
接口 | 作用 |
---|---|
ServletContextAware | 设置ServletContext |
ServletRequestAware | 设置HttpServletReque |
ServletResponseAware | 设置HttpServletResponse |
ParameterAware | 设置Map类型的请求参数 |
RequestAware | 设置Map类型的请求(HttpServletRequest)属性 |
SessionAware | 设置Map类型的会话(HttpSession)属性 |
ApplicationAware | 设置Map类型的应用程序作用域对象(ServletContext) |
4.fileUpload
将文件和元数据从多重请求(multipart/form-data)转换为常规的请求数据,以便将它们设置在对应的Action属性上,实现文件上传
5.validation
用于执行数据校验
6.workflow
提供当数据校验错误时终止执行流程的功能
7.Exception
用于捕获异常,并且能够根据异常将捕获的异常映射到用户自定义的错误页面。该拦截器执行时应该位于所定义的所有拦截器中的第一个
Struts 2内置拦截器栈
struts-default.xml中定义了一个非常重要的拦截器栈——defaultStack拦截器栈。defaultStack拦截器栈组合了多个拦截器,这些拦截器,这些拦截器的顺序经过精心的设计,能够满足大多数Web应用程序的需求,只要在定义包的过程中继承struts-default包
自定义拦截器
在Struts 2框架中所有的Struts 2拦截器都直接或间接地实现接口:com.opensymphony.xwork.interceptor.Interceptor
该接口提供了三种方法:
方法 | 描述 |
---|---|
void init() | 该拦截器被初始化之后,在该拦截器执行拦截之前,系统会回调该方法。对于每个拦截器而言,此方法只执行一次 |
void destroy() | 该方法跟init()方法对应。在拦截器实例被销毁之前,系统将回调该方法 |
String interceptor(ActionInvocation invocation) throws Exception | 该方法是用户需要实现的拦截器动作。该方法会返回一个字符串作为逻辑视图 |
除此之外,继承com.opensymphony.work2.interceptor.AbstractInterceptor类是更简单的一种实现拦截器的方式,AstractInterceptor类提供了init()和destroy()方法的空实现,这样就只需要实现interceptor()方法,就可以创建自己的拦截器。
在package中如果定义default-interceptor-ref默认拦截器,那么包中的所有Action都会默认调用拦截器。
实现Interceptor接口
(1)void init()
初始化拦截器所需资源
(2)void destroy()
释放在init()中分配的资源
(3)String intercept(ActionInvocation ai) throws Exception
①实现拦截器功能
②利用ActionInvocation参数获取Action状态
③返回结果码(result)字符串
继承AbstractInterceptor类
提供了init()和destroy()方法的空实现,只需要实现intercept方法即可。
(1)编写自定义拦截器,继承自AbstractInterceptor
public class MyTimerInterceptor extends AbstractInterceptor {
//继承AbstractInterceptor,重写interceptor方法
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("进入拦截器!!!");
// 预处理工作
long start = System.currentTimeMillis();
// 执行后续拦截器或Action
// 如果后续没有拦截器了,则执行Action返回的结果是Action返回的结果
//String result = invocation.invoke();
// 后续处理工作
long end = System.currentTimeMillis() - start;
System.out.println("times:" + end);
return "ok";
}
}
(2)在配置文件中定义拦截器
<!-- 在package下,定义拦截器 -->
<interceptors>
<interceptor name="time"
class="cn.my.interceptor.MyTimerInterceptor">
</interceptor>
</interceptors>
(3)引用拦截器
<!-- 在Action下 -->
<interceptor-ref name="time"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>