Struts2 拦截器详解 编程实例

来源:互联网 发布:内核linux 编辑:程序博客网 时间:2024/05/16 00:41

Struts2 拦截器

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

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

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

 

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

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

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

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

 

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

1-1先定义一个接口

package com.snt.struts2.interceptor;

 

public interface ITargetInterface {

         public void doSomething();

}

 

1-2 定义一个目标对象实现目标对象接口 [目标对象一定要实现一个接口]

package com.snt.struts2.interceptor;

public class Target implements ITargetInterface{

         //拦截的目标对象

         public void doSomething() {

                   System.out.println("do something...");

         }

}

 

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

package com.snt.struts2.interceptor;

public class Interceptor {

          public void befor(){

                    System.out.println("before");

          }

          public void after(){

                    System.out.println("after");

          }

}

 

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

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

使用以下更简单的方法:

     ITargetInterface if = (ITargetInterface)

Proxy.newProxyInstance(Foo.class.getClassLoader(),

                                          new Class[] { ITargetInterface.class },

                                          handler);

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

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

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

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

package com.snt.struts2.interceptor;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

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;

     }

}

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

package com.snt.struts2.interceptor;

import java.lang.reflect.Proxy;

/**

 * 产生一个代理

 * @author LEPING LI

 *

 */

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()之前和之后插入拦截的功能。

 

4OK,上面讲的一个采用我们自定义拦截器的方式实现在目标拦截的功能,下面的我们看一下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并没有把两种概念进行区别,而是都它们都当成拦截器对待,因此拦截器栈中可以声明拦截器,也可心声明拦截器链引用拦截器和拦截器链是同样的方式。

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

<?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">

            <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">welcome</param>

</interceptor-ref>

            </action>

        </package>

   </struts>

 

方式1 如下配置,我们将一个拦截配置在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”/>

 

同样我们也可以先定义拦截器,再在具体的Action中引用。

方式2如下配置:

<?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>

            </action>

        </package>

   </struts>

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

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

 

方式3:我们为简便,还可以定义一个拦截器栈,上面说过,拦截器栈和拦截一样,所有我们也在<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"></interceptor-ref>

                    <interceptor-ref

name="myInterceptor2"></interceptor-ref>

                    <interceptor-ref name="defaultStack" />

                </interceptor-stack>

            </interceptors>

            <!-- 设置默认拦截器栈  只参配置一个 应用所有的没有配置拦截器的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我们可以使用上面定义的拦截器,但是这里我们没有在下面的action,运行程序point Action还是被拦截了.这是为什么呢?看上面!有这样一句配置:

<default-interceptor-ref

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

 

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

 

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

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

每个拦截器执行前操作-----每二个拦截器执行前操作------每二个拦截器执行后操作-----每一个拦截器执行后操作

拦截器A   beforeA()   afterA()            拦截器B   beforeB()    afterB()

上面代表拦截器A要在目标对象执行前调用beforeA  在目标对象调用后执行afterA,拦截器B同样。

比如:一个action配置了A  B  两个拦截器

那么执行过程拦截器的调用情况如下:

beforeA()

beforeB()

    目标对象的调用

afterB();

afterA();

加头看上面的我们定怕拦截器,里面有三个方法(这是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-stack name="myStack">

                   <interceptor name="myInterceptor"

class="com.snt.struts2.interceptors.MyInterceptor">

                    <param name="hello">world</param>

                     <interceptor-ref name="defaultStack" />

                </interceptor>

            <!-- 设置默认拦截器栈  只参配置一个 应用所有的没有配置拦截器的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">test</param>

                </interceptor-ref>

            </action>

        </package>

   </struts>

注意:同时指定拦截又排除方法拦截时,会拦截方法  包含的级别高

上面举的例子拦截器中东西都比较简单,真正在开发时候需要根据具体的业务来编写拦截器的内容!

 

 

 

 

原创粉丝点击