拦截器详解

1、 拦截器的概念

Ø 拦截器(Interceptor)是动态拦截Action调用的对象,类似于Servlet中的过滤器。在执行Action的业务逻辑处理方法(execute())之前,Struts2会首先执行在struts.xml中引用的拦截器。

Ø 拦截器是struts2的一个重要特性。Struts2框架的大多数核心功能都是通过拦截器来实现的,像避免表单重复提交、类型转换、对象组装、验证、文件上传等,都是在拦截器的帮助下实现的。拦截器之所以称为“拦截器”,是因为它可以在Action执行之前和执行之后拦截调用。

Ø Struts2将它的核心功能放到拦截器中实现,而不是分散到Action中实现,有利于系统的解耦,使得功能的实现类似于个人电脑的组装,变成了可插拔的,需要某个功能就“插入”一个拦截器,不需要某个功能就“拔出”一个拦截器。你可以任意组合拦截器来为Action提供附加的功能,而不需要修改Action的代码。

2、 拦截器的工作方式

拦截器围绕着Action和Result的执行而执行,其工作方式如图:

从图中可以看到,在Action和Result执行之前,为Action配置的拦截器将首先被执行,在Action和Result执行之后,拦截器将重新获得控制权,然后按照与先前调用相反的顺序依次执行。在整个执行过程中,任何一个拦截器都可以选择直接返回,从而终止余下的拦截器、Action和Result的执行。例如:当一个未授权的用户访问受保护的资源时,执行身份验证的拦截器可以直接返回。

clip_image002

3、 编写拦截器类

在struts2中要编写拦截器类,必须实现com.opensymphony.xwork2.interceptor.Interceptor接口,该接口定义了如下的三个方法:

void init():

该方法在拦截器实例创建后、intercept()方法被调用之前调用,用于初始化拦截器所需要的资源,例如数据库连接的初始化。该方法只执行一次。

void destroy():

该方法在拦截器实例被销毁之前调用,用于释放在init()方法中分配的资源。该方法只执行一次。

String intercept(ActionInvocation invocation) throws Exception:

该方法在Action执行之前被调用,拦截器为Action提供的附加功能在该方法中实现。利用invocation参数,可以获取action执行的状态。在intercept()方法中,如果要继续执行后续的部分(包括余下的应用于Action的拦截器、Action和Result),可以调用invocation.invoke()(返回一个字符串,该字符串是Action中返回的字符串)。如果要终止后续的执行,可以直接返回一个结果码,框架将根据这个结果码来呈现对应的结果视图。

4、 例子

TimerInterceptor.java

public class TimerInterceptor implements Interceptor

{

public void destroy(){}

public void init(){}

public String intercept(ActionInvocation invocation) throws Exception

{

long startTime = System.currentTimeMillis();

String result = invocation.invoke();

long executionTime = System.currentTimeMillis() - startTime;

System.out.println("Action的执行花费的毫秒数是:" + executionTime);

return result;

}

}

Struts.xml

<struts>

<constant name="struts.i18n.encoding" value="GBK" />

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

<interceptors>

<interceptor name="time" class="com.ibm.action.TimerInterceptor"></interceptor>

</interceptors>

<action name="time" class="com.ibm.action.TimeAction">

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

<result>index.jsp</result>

</action>

</package>

</struts>

说明:

这是一个非常简单的拦截器,用于输出Action执行花费的时间。在invocation.invoke()调用的前后,你可以添加自己的逻辑代码。Invocation.invoke()调用之前的代码将在Action执行之前执行,invocation.ivoke()调用之后的代码将在Action执行之后执行。

为了简化拦截器的开发,struts2还提供了一个抽象类:

com.opensymphony.xwork2.interceptor.AbstractInterceptor

它实现了Interceptor接口,并给出了init()和destroy()方法的空实现。我们编写的拦截器类也可以选择继承AbstractInterceptor类,如果不需要init()和destroy()方法,那么你只需要重写抽象的intercept()方法就可以了。

在struts2中还提供了一个特殊的拦截器抽象基类:

com.opensymphony.xwork2.interceptor.MethodFilterInterceptor

这个拦截器可以指定要拦截或排除的方法列表。通常情况下,拦截器将拦截Action的所有方法调用,但是在某些应用场景中,对某些方法的拦截将会出现一些问题。例如:我们只需要对Action中部分方法进行拦截,将要如何处理啦?

MethodFilterInterceptor通过指定include/excluded方法列表来选择拦截或排除的方法,可以设置的参数如下:

excludeMethods

要排除的方法

includeMethods

要拦截的方法

在设置拦截或排除的方法时,如果有多个方法,那么以逗号(,)分隔,如果一个方法的名字同时出现在excludeMethods和includeMethods参数中,那么会当作要拦截的方法。

例如:有如下的拦截器配置:

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

<interceptors> <! - -在这里定义拦截器- ->

<interceptor name=“around” class=“AroundInterceptor”/>

</interceptors>
        < action name =“test” class =“cn.com.AroundAction” >
           < result > /Timer.jsp </ result >

< interceptor-ref name =“around”>

<param name=“includeMethods”>select,update</param>

<param name=“excludeMethods”>delete,insert</param>

</interceptor-ref>

</ action >

</ package>

4、拦截器栈

在很多时候,且些指定的拦截器需要被多个Action所使用,这个时候,如果我们为每一个Action都分别配置拦截器的话,不仅麻烦,而且不利后期的维护,此时就需要用到拦截器栈。

所谓拦截器栈就是将一些拦截器组合起来进行统一管理。

拦截器栈的使用与拦截器的使用非常相似,唯一有区别的地方,就是拦截器栈的布署方式。

<interceptors>

<interceptor name=“permission" class="cn.itcast.aop.PermissionInterceptor" />

<interceptor-stack name="permissionStack">

<interceptor-ref name="defaultStack" />

<interceptor-ref name=" permission " />

</interceptor-stack>

</interceptors>

<action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">

<result name="success">/WEB-INF/page/hello.jsp</result>

<interceptor-ref name="permissionStack"/>

</action>

5、 内置的拦截器

clip_image004

clip_image006

6、配置拦截器

拦截器需要在struts.xml中进行配置才能为action提供服务。要为action配置引用拦截器,首先需要在interceptors元素中使用interceptor元素定义拦截器,然后 在action中使用interceptor-ref元素指定引用的拦截器。Interceptor元素有两个必需的属性:name和class,前者指定拦截器的名字,后者指定拦截器的完整类名。

比如对如前面的拦截器,需要如下配置:

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

<interceptors> <! - -在这里定义拦截器- ->

<interceptor name=“around” class=“AroundInterceptor”/>

</interceptors>
        < action name =“test” class =“cn.com.AroundAction” >
           < result > /Timer.jsp </ result >

< interceptor-ref name =“around” /> <!– 此处引用拦截器 -- >
        </ action >

</ package >

如果在一个action需要多个拦截器,那又需要如何配置啦?另外,对于多个拦截器之间的执行顺序又是怎么样啦?

对于上述问题有二个解决方法:

(1)为一个Action配置多个拦截器

(2)将多个拦截器配为一个拦截器栈,然后在action中引用

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

<interceptors> <! - -在这里定义拦截器- ->

<interceptor name=“before” class=“BeforeInterceptor”/>

<interceptor name=“after” class=“AfterInterceptor”/>

<!—配置拦截器栈,便于使用à

<interceptor-stack name=“mystack”>

< interceptor-ref name =“before” />

< interceptor-ref name =“after” />
</interceptor-stack>

</interceptors>

<default-interceptor-ref name=“mystack”/>

        < action name =“test” class =“cn.com.TestAction” >
            < result > /Timer.jsp </ result >

<!—如果配置了此拦截器,则默认的拦截器栈将失效à

< interceptor-ref name =“after” />

          </ action >

</ package >

6、 开发拦截器类

大家在开始着手创建自定义拦截器前,切记以下原则:
拦截器必须是无状态的,不要使用在API提供的ActionInvocation之外的任何东西。

原因:Struts 2不能保证为每一个请求或者action创建一个实例,所以如果拦截器带有状态,会引发并发问题。

在Struts2中编写拦截类,有如下二种方式:

1>实现Interceptor此接口

2>继承AbstractInterceptor类(一般会选择此种方式)

例如:

public class AroundInterceptor extends AbstractInterceptor {

public void init(){System.out.println(“这是一个初始化方法”)}

public String intercept(ActionInvocation invocation) throws Exception {

String result="";

System.out.println("我是在action的execute方法执行之前动作");

result=invocation.invoke();

System.out.println("我是在action的execute方法执行之后动作");

return result;

}

public void destroy(){System.out.println(“这是一个清理方法”)}

}

注意:在编写拦截器时要注意,拦截器必顺是无状态的,换句话 说,在拦截器类中不应该有实例变量。这是因为struts2对每一个Action的请求使用的是同一个拦截器实例来处理,如果拦截器有状态,在多线程并发情况下,拦截器的状态将不可测。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值