Struts2拦截器详解

jopen 9年前

Struts2 拦截器

1.Struts中拦截器就是一个类,实现了Interceptor 接口的一个类。

2.Struts中拦截器和Servlet中的Filter有类似的功能,从字面意思来看,Struts 拦截器就是在目标对应执行之前或之后做一些事情,其实Struts中的拦截器的实现也是一样,在具体Action的被调用之前或之后可以做一些操作,采用配置化的方法进行管理,使用起来比较简单。但是我们在学习过程中最好去了解拦截底层实现原理,以便在使用过程中能够控制自己的程序。从了解拦截器的实现原理入手会是我们学习拦截器的最好途径。

3.拦截器采用的动态代理模式实现的在目标执行之前或之后插入必要的辅助业务。其实采用的是一种AOP的思想,来降低系统耦合。

下面我们介绍一个动态代理:

Java中提供一种动态代理的实现模式,我们用一个例子实现动态代理的演示:

涉及到代理就肯定要有:目标对象   代理对象   拦截器

三者之间的关系:  代理对象 代理 目标对象  在目标对象执行之前或之后 加入拦截器的功能

首先我们创建一个目标对象:

3.1 定义一个接口

package com.snt.struts2.interceptor;    public interface ITargetInterface {   public void doSomething();  }

3.2 定义一个目标对象实现目标对象接口 [目标对象一定要实现一个接口]

package com.snt.struts2.interceptor;    public class Target implements ITargetInterface {      // 拦截的目标对象   public void doSomething() {    System.out.println("do something...");   }  }

3.3 定义一个拦截器(这里我们的拦截器比较简单,就是一个普通在类,定义了一在目标对象调用之前工之后要执行的操作) [定义的两个方法,代表在目标对象调用之前和之后要执行的操作]

package com.snt.struts2.interceptor;    public class Interceptor {     public void befor() {    System.out.println("before");   }     public void after() {    System.out.println("after");   }  }

3.4 下面来实现代理,如何为目标对象产生一个代理对象呢?java为我们提供了一定代理机制!

Java在java.lang.reflect包下面提供了一个Proxy 类,这个类可以为一个目标类产生代理类。

使用以下更简单的方法:

ITargetInterface iTargetInterface = (ITargetInterface) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { ITargetInterface.class }, handler);

第一个参数:目标类的加载器 第二个参数:目标实现的接口集合 第三个参数:代理类的调用处理程序对象.

动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。代理接口 是代理类实现的一个接口。代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的Invoke方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

对于我们这个例子:也要先创建一个代理类的调用处理程序,如下:

3.5 创建一个MyHandler类,实现InvocationHandler接口,实现其中的invoke()方法,代码如下:

package com.snt.struts2.interceptor;    import java.lang.reflect.InvocationHandler;  import java.lang.reflect.Method;    public class MyHandler implements InvocationHandler {   private Object obj;// 目标对象     // 拦截器对象   private Interceptor interceptor = new Interceptor();     // 注入目标对象   public void setObject(Object obj) {    this.obj = obj;   }     public Object invoke(Object proxy, Method method, Object[] args)     throws Throwable {    Object result = null;    // 调用目标方法前执行的操作    interceptor.befor();    result = method.invoke(obj, args);    // 调用目标方法后执行的操作    interceptor.after();    return result;   }  }

3.6 创建一个产生代理的工厂类,创建一个MyProxy类,代码如下:

package com.snt.struts2.interceptor;    import java.lang.reflect.Proxy;    /**   * 产生一个代理   * @author Wang   *   */  public class MyProxy {     // 根据一个目标类产生一个代理对象   public Object getProxy(Object object) {    MyHandler mh = new MyHandler();    mh.setObject(object);    return Proxy.newProxyInstance(Target.class.getClassLoader(), object      .getClass().getInterfaces(), mh);   }  }

创建一个测试类,为目标对象产生代理,调用其方法,查看执行效果:

package com.snt.struts2.interceptor;    public class Client {     public static void main(String[] args) {    ITargetInterface target = new Target();    MyProxy mp = new MyProxy();    ITargetInterface proxy = (ITargetInterface) mp.getProxy(target);    proxy.doSomething();   }  }

运行结果:在调用目标类方法doSomethin()之前和之后插入拦截的功能。

 

4.OK,上面讲的一个采用我们自定义拦截器的方式实现在目标拦截的功能,下面的我们看一下Struts2中如何使用拦截器。Struts2中拦截器是一个实现了Interceptor接口的类,下面我们来定义一具体的拦截器,实现Action的拦截。

定义一个MyInterceptor 类,代码如下:

package com.snt.struts2.interceptors;    import com.opensymphony.xwork2.ActionInvocation;  import com.opensymphony.xwork2.interceptor.Interceptor;    public class MyInterceptor implements Interceptor {     private String hello;     public String getHello() {    return hello;   }     public void setHello(String hello) {    this.hello = hello;   }     public void destroy() {    System.out.println("destory()");   }     public void init() {    System.out.println("init()");    System.out.println(hello);   }     /**    *     * 拦截Action    */   public String intercept(ActionInvocation invocation) throws Exception {    System.out.println("interceptor");    String result = invocation.invoke();    System.out.println("finshed");    return result;   }  }

拦截器中主要实现interceptor()方法,在interceptor中调用目标对象前后加入你定义的业务操作。

OK,拦截器配置好之后,如何让其生效呢?需要在struts.xml 文件中配置。

在struts.xml 配置文件中,Struts2会为每个Action加上一具默认在拦截器配置,那就是struts-default.xml中的<interceptor-ref  name="defaultStack" />,在Struts2拦截器存在两种概念,一种是拦截器,一种拦截链,拦截链就是一系列的拦截器连接在一起进行对目标拦截,而Struts2并没有把两种概念进行区别,而是都它们都当成拦截器对待,因此在拦截器的定义标签<interceptors></interceptors>中,定义、引用拦截器和拦截链是同样的方式。

对于上面的拦截器,我们需要在struts.xml 文件中如下配置

方式1:

<?xml version="1.0" encoding="UTF-8" ?>  <!DOCTYPE struts PUBLIC      "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"      "http://struts.apache.org/dtds/struts-2.0.dtd">  <struts>   <package name="struts2" extends="struts-default">    <!-- 配置拦截器 -->    <interceptors>     <!-- 自定义拦截器 -->     <interceptor name="myInterceptor" class="com.snt.struts2.interceptors.MyInterceptor">      <param name="hello">world</param>     </interceptor>    </interceptors>    <action name="point" class="com.snt.struts2.action.PointAction">     <result name="success">output.jsp</result>     <result name="input">input.jsp</result>     <!-- 引用上面配置的拦截器 -->     <interceptor-ref name="myInterceptor">      <param name="hello">welcom</param>     </interceptor-ref>     <interceptor-ref name="defaultStack"></interceptor-ref>    </action>   </package>  </struts>

我们将一个拦截配置在point这个Action中,说明我们要使用这个拦截器来拦截这个Action,见红色的字体,拦截器配置中间我们还加了一个参数,struts2会自动将该参数的值加载到拦截器中对应属性的值。OK,这样配置好了,运行程序测试,发现结果数据是错误的。这是为什么呢?

前面说过Struts2会为每个Action应用一个默认的拦截器,这个拦截器是在struts-default.xml文件中配置的,而我们的struts.xml 开关又出现这样的配置

<package name="struts2" extends="struts-default">

说明我们的配置继承了struts-default.xml 中的配置。

当我们如下配置文件中那样配置后,默认的配置就会被引用了,这是Struts2本身所有实现的,当我们为一个Action配置了拦截器时,默认的拦截器就会失效,但是Struts2本身的一些功能,比如说参数自动赋值又是依赖配置的默认拦截器实现,所有应用程序会出错。这时需要我们手动将默认的拦截器引用进来,再为这项Action加入默认拦截器:

<interceptor-ref name="defaultStack"/>

另外要注意:如果拦截器声明时和引用时都配置了参数,那最引用时的参数配置是有效的。

比如:上面的配置在配置拦截器时给hello参数传一个”world”,但在下面action中使用拦截器时,又给参数hello配置了"welcome”值,最终使用的将是action使用时拦截器时配置的值。

方式2:

我们为简便,还可以定义一个拦截链,上面说过,拦截链和拦截器一样,所以我们也在<interceptors></interceptors>标签中配置拦截链。

<?xml version="1.0" encoding="UTF-8" ?>  <!DOCTYPE struts PUBLIC      "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"      "http://struts.apache.org/dtds/struts-2.0.dtd">  <struts>   <package name="struts2" extends="struts-default">    <!-- 配置拦截器 -->    <interceptors>     <!-- 自定义拦截链 -->     <interceptor-stack name="myStack">      <!-- 执行顺序与配置顺序有关 -->      <interceptor-ref name="myInterceptor" class="com.snt.struts2.interceptors.MyInterceptor"></interceptor-ref>      <interceptor-ref name="myInterceptor2" class="com.snt.struts2.interceptors.MyInterceptor2"></interceptor-ref>      <interceptor-ref name="defaultStack"></interceptor-ref>     </interceptor-stack>    </interceptors>      <!-- 设置默认拦截链,一个package下只能配置一个,应用所有的没有配置拦截器或拦截链的Action -->    <default-interceptor-ref name="myStack"></default-interceptor-ref>      <action name="point" class="com.snt.struts2.action.PointAction">     <result name="success">output.jsp</result>    </action>   </package>  </struts> 

上面我们配置一个拦截链,但是我们并没有写在下面的action中,运行程序PointAction还是被拦截了。这是为什么呢?看上面有这样一句配置:

<default-interceptor-ref name="myStack"></default-interceptor-ref>

这句配置是定义了一个默认拦截链或拦截器,这个拦截就像struts-default.xml文件中的defaultStack一样,我们在我们的配置中也可以定义一个默认拦截器来拦截所有的action。但是还是要注意一点,当一个action引用了其它的拦截器或拦截链时,我们定义的拦截器也会失效。而且默认的拦截器一个包中只能声明一个。

还要注意的是:我们定义多个拦截器后它们的执行顺序,这个顺序和他们在配置中声明的顺序是有关的,先声明的先执行。

 

拦截器中执行的操作分两种,一种是目标对象调用前执行的操作,一种目标对象调用后执行的操作。这个也是有顺序的。如果有两个拦截器,那么执行的顺序就是:第一个拦截器执行前操作——第二个拦截器执行前操作——目标对象的调用——第二个拦截器执行后操作——第一个拦截器执行后操作

比如一个action配置了A、B两个拦截器:拦截器A  beforeA()  afterA()      拦截器B  beforeB()  afterB()

上面代表拦截器A要在目标对象执行前调用beforeA(), 在目标对象调用后执行afterA(),拦截器B同样。那么执行过程拦截器的调用情况如下:

beforeA()

beforeB()

      目标对象的调用

afterB()

afterA()

 

上面的我们定义的拦截器类MyInterceptor里面有三个方法(这是Interceptor接口中的三个方法),即:

init();

destory();

interceptor(ActionInvocation invocation)              【我们真正要实现的方法】

但是init()和destory()方法我们不经常用,但是双必须把它继承下来,看起不是很爽,查看API我们会找到一个AbstrctInterceptor类,这个类是一个抽象类,它实现了init()和destory()【其实两个方法并没有没真正做什么,是空实现】,interceptor() 方法是抽象的,所以我通常自定义拦截器时,可以实现AbstractInterceptor类,着重写interceptor()方法即可。

 

OK,上面讲的都是针对Action的拦截,Struts2中还有一种拦截器是针对方法的拦截器。

Struts2中有一个MethodFilterInterceptor的拦截器,它继承了AbstractInterceptor类,可以对方法进行拦截。这个类也是个抽象类,其中有doInterceptor()方法是个抽象方法,我们只需要实现这个方法即可。

比如:我们需要使用一个拦截器拦截一个action中的login()方法,而不拦截器action中的logout()方法,看拦截器如何实现:

MethodFileterInterceptor 拦截器是通过指定

// 不需要拦截的方法名列表  protected Set<String> excludeMethods = Collections.emptySet();    // 需要拦截的方法名列表  protected Set<String> includeMethods = Collections.emptySet();

以上两个属性,一个是指定哪些方法需要拦截,另一个是指定哪些方法不需要拦截。在配置拦截器时需要配置这两个参数。

我们行定义一个方法拦截器,代码如下:MyInterceptor3

package com.snt.struts2.interceptors;    import com.opensymphony.xwork2.ActionInvocation;  import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;    public class MyInterceptor3 extends MethodFilterInterceptor {   @Override   protected String doIntercept(ActionInvocation invocation) throws Exception {    System.out.println("my Interceptor3");    String result = invocation.invoke();    return result;   }  }

下面我们配置这个方法拦截器:

<?xml version="1.0" encoding="UTF-8" ?>  <!DOCTYPE struts PUBLIC      "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"      "http://struts.apache.org/dtds/struts-2.0.dtd">  <struts>   <package name="struts2" extends="struts-default">    <!-- 配置一个拦截器 -->    <interceptors>     <!-- 自定义拦截器 -->     <interceptor name="myInterceptor3" class="com.snt.struts2.interceptors.MyIntercepto3" />     <interceptor name="myInterceptor" class="com.snt.struts2.interceptors.MyInterceptor">      <param name="hello">world</param>     </interceptor>       <!-- 自定义拦截链 -->     <interceptor-stack name="myStack">      <interceptor-ref name="myInterceptor"></interceptor-ref>      <interceptor-ref name="defaultStack"></interceptor-ref>     </interceptor-stack>    </interceptors>      <!-- 设置默认拦截链,一个package下只能配置一个,应用所有的没有配置拦截器或拦截链的Action -->    <default-interceptor-ref name="myStack"></default-interceptor-ref>      <action name="point" class="com.snt.struts2.action.PointAction">     <interceptor-ref name="myStack" />     <interceptor-ref name="myInterceptor3">      <!-- 同时指定拦截方法和排除方法时,拦截方法的级别高 -->      <param name="includeMethods">execute,login</param>      <param name="excludeMethods">logout</param>     </interceptor-ref>    </action>   </package>  </struts> 

注意:同时指定拦截方法和排除方法时,拦截方法的级别高。

上面举的例子拦截器中东西都比较简单,真正在开发时候需要根据具体的业务来编写拦截器的内容!所以大家在实际项目中多多练习便会慢慢的掌握拦截器,另外Struts2中自带了的拦截器、拦截链(在struts-default.xml中),大家还是应该了解下的,具体如下:

拦截器

名字

说明

Alias Interceptor

alias

在不同请求之间将请求参数在不同名字件转换,请求内容不变

Chaining Interceptor

chain

让前一个Action的属性可以被后一个Action访问,现在和chain类型的result<result type=”chain”>)结合使用。

Checkbox Interceptor

checkbox

添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox

Cookies Interceptor

cookies

使用配置的name,value来是指cookies

Conversion Error Interceptor

conversionError

将错误从ActionContext中添加到Action的属性字段中。

Create Session Interceptor

createSession

自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。

Debugging Interceptor

debugging

提供不同的调试用的页面来展现内部的数据状况。

Execute and Wait Interceptor

execAndWait

在后台执行Action,同时将用户带到一个中间的等待页面。

Exception Interceptor

exception

将异常定位到一个画面

File Upload Interceptor

fileUpload

提供文件上传功能

I18n Interceptor

i18n

记录用户选择的locale

Logger Interceptor

logger

输出Action的名字

Message Store Interceptor

store

存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。

Model Driven Interceptor

model-driven

如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。

Scoped Model Driven

scoped-model-driven

如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用ActionsetModel方法将其放入Action内部。

Parameters Interceptor

params

将请求中的参数设置到Action中去。

Prepare Interceptor

prepare

如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。

Scope Interceptor

scope

Action状态存入sessionapplication的简单方法。

Servlet Config Interceptor

servletConfig

提供访问HttpServletRequestHttpServletResponse的方法,以Map的方式访问。

Static Parameters Interceptor

staticParams

struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中。

Roles Interceptor

roles

确定用户是否具有JAAS指定的Role,否则不予执行。

Timer Interceptor

timer

输出Action执行的时间

Token Interceptor

token

通过Token来避免双击

Token Session Interceptor

tokenSession

Token Interceptor一样,不过双击的时候把请求的数据存储在Session

Validation Interceptor

validation

使用action-validation.xml文件中定义的内容校验提交的数据。

Workflow Interceptor

workflow

调用Actionvalidate方法,一旦有错误返回,重新定位到INPUT画面

Parameter Filter Interceptor

N/A

从参数列表中删除不必要的参数

Profiling Interceptor

profiling

通过参数激活profile

那么关于拦截器的介绍就到这里了,希望对大家能有所帮助