struts2(六)拦截器机制

来源:互联网 发布:大智慧软件好用吗 编辑:程序博客网 时间:2024/06/10 04:37


拦截器简述

拦截器是一组动态拦截Action调用的对象。拦截器的处理代码可以定义在action执行前或者执行后。同时,拦截器能够拦截一个Action的执行。拦截器可以将一些通用功能封装成可重用形式以供一个Action或多个 Actions使用。

这里写图片描述

拦截器必须是无状态的,原因是Struts 2不能保证为每一个请求或者Action创建一个实例,所以如果拦截器带有状态,会引发并发问题。不要使用在API提供的ActionInvocation之外的任何东西。

在概念上,interceptors相近于Servlet 的filters或者JDKs的Proxy类。可以把struts2理解成一个空容器,而大量的内建拦截器完成该框架的大部分操作。比如,params拦截器负责解析HTTP请求的参数,并设置Action的属性;servlet-config拦截器直接将HTTP请求中的HttpServletRequest实例和HttpServletResponse实例传给Action;fileUpload拦截器负责解析请求参数中的文件域,并将一个文件域设置为Action的三个属性等等。这些通用操作是通过struts2的内建拦截器完成的。

struts2拦截器是可插拔式的,如果需要使用某个拦截器,只需要在配置文件中应用该拦截器即可,如果不使用该拦截器只需要在配置文件中取消该拦截器,不用重新部署应用。

struts2拦截器由struts-default.xml、struts.xml等配置文件进行管理,开发者可以很容易地扩展自己的拦截器。

如前面所说开发中存在一些通用逻辑,如解析请求参数、类型转换、将请求参数封装成DTO(Data Transfer Object)、执行输入校验、解析文件上传表单中的文件域、防止表单的多次提交等。struts2把大部分核心控制器需要完成的工作功能分开定义,每个拦截器完成一个功能。这些拦截器可以自由选择,灵活组合(甚至不用struts2的任何拦截器),需要哪些拦截器只需在struts.xml文件中指定使用该拦截器即可。

在struts.xml文件中配置Action时,在package里继承struts-default包,包内的大量通用功能的拦截器就会起作用。

<package name="default" extends="struts-default">   <interceptors>       <interceptor name="timer" class=".."/>       <interceptor name="logger" class=".."/>   </interceptors>   <action name="login"      class="tutorial.Login">        <interceptor-ref name="timer"/>        <interceptor-ref name="logger"/>         <result name="input">login.jsp</result>         <result name="success"            type="redirectAction">/secure/home</result>   </action></package>

在web.xml文件中定义核心Filter。

<!-- 定义struts2核心Filter -->    <filter>        <filter-name>struts2</filter-name>        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>    </filter>    <!-- 让struts2的核心Filter拦截所有action请求 -->    <filter-mapping>        <filter-name>struts2</filter-name>        <url-pattern>*.action</url-pattern>    </filter-mapping>

当StrutsPrepareAndExecuteFilter拦截到用户请求后,大量拦截器将会对用户请求进行处理,然后才会调用用户开发的Action实例的方法处理请求。拦截器与Action之间的关系如下图所示。

这里写图片描述

struts2内建的拦截器

struts2内建了大量的拦截器,这些拦截器以name-class对的形式配置在struts-default.xml文件中,其中name是拦截器的名字,就是以后使用该拦截器的唯一表示;class指定了该拦截器的实现类,程序定义的package继承了struts2的默认struts-default包,就可以使用包内定义的拦截器,不用自己定义拦截器。

这里写图片描述

这里写图片描述

这里写图片描述

自定义拦截器

内建拦截器实现了struts2大部分功能,大部分Web应用的通用功能,都可以通过直接使用这些内建拦截器完成,但有一些系统逻辑相关的通用相关功能,要通过自定义拦截器来实现。

Interceptor接口

开发自定义拦截器类,要实现com.opensymphony.xwork2.interceptor.Interceptor接口,该接口的定义代码如下。

public interface Interceptor extends Serializable {    //销毁该拦截器之前的回调方法    void destroy();    //初始化该拦截器的回调方法    void init();      //拦截器实现拦截的逻辑方法    String intercept(ActionInvocation invocation) throws Exception;}


init()方法在该拦截器被实例化之后,执行拦截之前被回调。每个拦截器的init()方法只执行一次。所以该方法体主要用于初始化资源,例如数据库连接等。

destroy()对应于init()方法,在拦截器实例被销毁之前,系统将回调该拦截器的destroy方法,用于销毁在init()方法里打开的资源。

inercept(ActionInvocation invocation)方法是用户需要实现的拦截动作,像Action的execute方法,intercept方法返回一个字符串作为逻辑视图。如果该方法直接返回一个字符串,系统会跳转到该逻辑视图对应的实际 视图资源,不会调用被拦截的Action。该方法的ActionInvocation参数包含了被拦截的Action引用,可以通过调用该参数的invoke方法,将控制权转给下一个拦截器,或者转给Action的execute方法。

AbstractInterceptor类

AbstractInterceptor类实现了Interceptor接口。

这里写图片描述

这里写图片描述

AbstractInterceptor类提供了init()和destory()方法的空实现,如果实现的拦截器不需要打开资源则可以无需实现这两个方法。

这里写图片描述

定义和使用拦截器

自定义一个拦截器需要三步:

1. 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。

2. 在strutx.xml中注册上一步中定义的拦截器。

3. 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。

自定义拦截器

import com.opensymphony.xwork2.ActionInvocation;import com.opensymphony.xwork2.interceptor.AbstractInterceptor;import java.util.*;import com.afy.app.action.*;public class SimpleInterceptor extends AbstractInterceptor{    // 简单拦截器的名字    private String name;    // 为该简单拦截器设置名字的setter方法    public void setName(String name){        this.name = name;    }    public String intercept(ActionInvocation invocation) throws Exception{        // 取得被拦截的Action实例        LoginAction action = (LoginAction)invocation.getAction();        // 打印执行开始的时间        System.out.println(name + " 拦截器的动作---------" +            "开始执行登录Action的时间为:" + new Date());        // 取得开始执行Action的时间        long start = System.currentTimeMillis();        // 执行该拦截器的后一个拦截器        // 如果该拦截器后没有其他拦截器,则直接执行Action的被拦截方法        String result = invocation.invoke();        // 打印执行结束的时间        System.out.println(name + " 拦截器的动作-------" + "执行完登录Action的时间为:" + new Date());        long end = System.currentTimeMillis();        System.out.println(name + " 拦截器的动作---------" + "执行完该Action的时间为" + (end - start) + "毫秒");        return result;    }}

在strutx.xml中注册上一步中定义的拦截器,在需要使用的Action中引用上述定义的拦截器。

<?xml version="1.0" encoding="GBK"?><!DOCTYPE struts PUBLIC    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"    "http://struts.apache.org/dtds/struts-2.3.dtd"><struts>    <!-- 通过常量配置该应用所使用的字符集-->    <constant name="struts.i18n.encoding" value="GBK"/>    <!-- 配置本系统所使用的包 -->    <package name="lee" extends="struts-default">        <!-- 应用所需使用的拦截器都在该元素下配置 -->        <interceptors>            <!-- 配置mySimple拦截器 -->            <interceptor name="mySimple"            class="org.crazyit.app.interceptor.SimpleInterceptor">                <!-- 为拦截器指定参数值 -->                <param name="name">简单拦截器</param>            </interceptor>        </interceptors>        <action name="login" class="org.crazyit.app.action.LoginAction">            <result name="error">/WEB-INF/content/error.jsp</result>            <result>/WEB-INF/content/welcome.jsp</result>             <!-- 配置系统的默认拦截器 -->            <interceptor-ref name="defaultStack"/>            <!-- 应用自定义的mySimple拦截器 -->            <interceptor-ref name="mySimple">                <param name="name">改名后的拦截器</param>            </interceptor-ref>        </action>        <action name="*">            <result>/WEB-INF/content/{1}.jsp</result>        </action>    </package></struts>


拦截器的实现原理

大部分时候,拦截器方法都是通过代理的方式来调用的。Struts 2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。

这里写图片描述

我们之所以能够如此灵活地使用拦截器,完全归功于“动态代理”的使用。动态代理是代理对象根据客户的需求做出不同的处理。对于客户来说,只要知道一个代理对象就行了。那Struts2中,拦截器是如何通过动态代理被调用的呢?当Action请求到来的时候,会由系统的代理生成一个Action的代理对象,由这个代理对象调用Action的execute()或指定的方法,并在struts.xml中查找与该Action对应的拦截器。如果有对应的拦截器,就在Action的方法执行前(后)调用这些拦截器;如果没有对应的拦截器则执行Action的方法。其中系统对于拦截器的调用,是通过ActionInvocation来实现的。

与过滤器的区别

过滤器可以简单理解为“取你所想取”,忽视掉那些你不想要的东西;拦截器可以简单理解为“拒你所想拒”,关心你想要拒绝掉哪些东西,比如一个BBS论坛上拦截掉敏感词汇。

1. 拦截器是基于java反射机制的,而过滤器是基于函数回调的。

2. 过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。

3. 拦截器只对action起作用,而过滤器几乎可以对所有请求起作用。

4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能。

5. 在action的生命周期里,拦截器可以多起调用,而过滤器只能在容器初始化时调用一次。

1 0