详解Struts2拦截器机制
Struts2的核心在于它复杂的拦截器,几乎70%的工作都是由拦截器完成的。比如我们之前用于将上传的文件对应于action实例中的三个属性的fileUpload拦截器,还有用于将表单页面的http请求参数设置成action中对应的属性的param拦截器等。总之,在整个Struts框架中拦截器的作用是相当大的,本篇将从以下几点详细介绍下有关Struts拦截器的内容:
- 拦截器在Struts中的作用
- 自定义拦截器实现类
- 配置拦截器(包含配置默认拦截器)
- 引用拦截器
- 配置拦截指定方法的拦截器
- 拦截器的拦截顺序
一、拦截器在Struts中的作用
在我们的web.xml中,我们配置了一个过滤器,实现将所有请求交付StrutsPrepareAndExecuteFilter类。一旦接受到任意action的请求,该类会创建和初始化一个ActionProxy实例,它代理了具体的action,在其中我们可以添加任意拦截器在execute方法执行之前和之后做一些额外的操作,最终会调用该action实例的execute方法,为用户返回视图结果字符串,然后系统会根据该视图结果字符串调取相应的视图页面。下图是拦截器和action之间的关系:
这是一种典型的AOP思想,当我们在Struts.xml中定义一个包的时候,大部分情况下我们会继承struts-default文件,所以虽然我们在自己的配置文件中并没有手动配置任何的拦截器,但是我们创建的action却被很多拦截器拦截处理,就是因为struts-default中配置的拦截器生效了。Struts中内建了很多的拦截器,他们大多被配置在struts-default文件中,详细的内建拦截器的介绍可以参考官方API,接下来我们看如何自定义一个拦截器。
二、自定义拦截器实现类
想要实现自己的拦截器类只需要实现 com.opensymphony.xwork2.interceptor.Interceptor.Interceptor 接口即可,该接口中有如下几个方法:
public abstract void destroy();
public abstract void init();
public abstract String intercept(ActionInvocation paramActionInvocation)
throws Exception;
init 方法在执行拦截方法之前回调,主要用于初始化一些资源,destroy 与init 方法对应,在拦截器实例被销毁之前回调,主要用于释放在init 方法中打开的资源。intercept 方法是我们的拦截方法,我们可以重写该方法来完成对action实例的拦截,该方法具有一个ActionInvocation 类型的参数,该参数内部引用了具体的action实例对象(如果该action还有其他拦截器的话),我们可以调用该参数的invoke方法调用具体action实例的execute方法或者调用下一个拦截器,intercept方法返回一个String 类型的字符串代表了具体视图页面。下面看个具体的例子:
public class TestAction extends ActionSupport {
public String execute(){
System.out.println("执行execute方法......");
return SUCCESS;
}
}
public class MyIntercept implements Interceptor {
public void init() {}
public void destroy() {}
public String intercept(ActionInvocation action) throws Exception{
System.out.println("拦截action开始.......");
String result = action.invoke();
System.out.println("拦截action结束.......");
return result;
}
}
省略了配置拦截器和TestAction 的代码,下图是上述程序运行的结果截图:
三、配置和引用拦截器
上述的示例定义了一个简单的拦截器实现类,我们省略了在struts.xml中配置和引用该拦截器的代码,本小节将详细的介绍如何在struts.xml中定义和引用我们自定义实现的拦截器类。
从struts-default.xml中我们可以看出来,我们使用<interceptors>元素定义拦截器name和物理位置的配对,例如:
<interceptors>
<interceptor name="test" class="MyPackage.TestAction"/>
......
......
</interceptors>
上述代码定义了一个拦截器test,它对应于具体的一个class。需要注意的是,定义拦截器的元素 interceptors 及其子元素必须被配置在某个package包下。
以上只是定义了一个拦截器和具体拦截器实现类之间的映射关系,但是想要实现对某个具体的action的拦截需要使用元素<interceptor-ref>根据name属性值引用一个上述已经定义了的拦截器。例如:
<action name="test" class="MyPackage.TestAction">
<interceptor-ref name="test"/>
<result name="success">/index.jsp</result>
......
......
</action>
正如上述代码展示的一样,该元素用于引用一个已经定义好了的拦截器,并且该元素出现在具体的action内部,表明了该action具有一个test拦截器。以上代码实现了对单个拦截器的定义和引用,其实对于拦截器栈(一堆拦截器的组合)来说配置也是类似的。定义一个拦截器栈的代码是如下的:
<interceptor-stack name="拦截器栈名">
interceptor-ref name="拦截器一"/>
interceptor-ref name="拦截器二"/>
interceptor-ref name="拦截器三"/>
.....
</interceptor-stack>
引用一个拦截器栈就没什么区别了:
interceptor-ref name="拦截器栈名"/>
当然我们也可以通过
<default-interceptor-ref name="拦截器名"/>
配置默认拦截器或者拦截器栈,如果该包下某个action没有显式指定拦截器,那么就会调用该默认拦截器,否则如果显式配置了拦截器,那么默认拦截器将会失效。
四、为Action中指定方法配置拦截器
在默认情况下,我们为action配置了拦截器之后,该拦截器将会拦截该action中所有的方法,这有时候会给我们带来麻烦,当然struts为我们提供API用来针对具体的某个方法配置拦截器。这里涉及到一个抽象类:MethodFilterInterceptor。该类实际上实现了Interceptor并完成了一些默认实现,我们简单看看其中的代码:
public abstract class MethodFilterInterceptor
extends AbstractInterceptor
{
//该set集合保存了该拦截器不需要拦截的所有方法
protected Set<String> excludeMethods = Collections.emptySet();
//该set集合保存了所有该拦截器需要拦截的方法
protected Set<String> includeMethods = Collections.emptySet();
//省略getter,setter方法
//用于拦截action的入口
public String intercept(ActionInvocation invocation)
throws Exception
{
if (applyInterceptor(invocation)) {
return doIntercept(invocation);
}
return invocation.invoke();
}
//判断当前需要调用的action处理逻辑方法是否需要被此拦截器拦截
protected boolean applyInterceptor(ActionInvocation invocation)
{
String method = invocation.getProxy().getMethod();
boolean applyMethod = MethodFilterInterceptorUtil.applyMethod(this.excludeMethods, this.includeMethods, method);
if ((this.log.isDebugEnabled()) &&
(!applyMethod)) {
this.log.debug("Skipping Interceptor... Method [" + method + "] found in exclude list.", new String[0]);
}
return applyMethod;
}
//这是需要我们重写的方法,具体作用下文介绍
protected abstract String doIntercept(ActionInvocation paramActionInvocation)
throws Exception;
}
从上述代码中可以看出,该抽象类实现了Interceptor接口并完成了基本的实现。除此之外,该类提供了两个集合用于保存该拦截器需要拦截的所有方法和不需要拦截的所有方法,拦截器入口intercept中会首先判断此次请求action实例中的逻辑处理方法是否需要被该拦截器拦截,如果需要被拦截,那么将会调用doIntercept我们自己实现的拦截器逻辑。否则直接调用invoke方法执行处理逻辑。所以一般来说,我们只需要重写doIntercept方法完成拦截器的核心处理即可。
当然此处需要注意一点的是,用于判断当前请求的处理逻辑方法是否需要被该拦截器拦截的方法applyInterceptor是在intercept中进行校验的,也就是说在执行doIntercept方法之前excludeMethods和includeMethods的值应当是已经初始化完毕了的。所以我们在doIntercept中再次为这两个属性赋值是没用的,因为已经完成了校验。一般我们在struts.xml中为这两个属性赋值,因为该配置文件是先被加载的。下面我们看个实例:
//自定义一个拦截器
public class MyIntercept extends MethodFilterInterceptor {
protected String doIntercept(ActionInvocation action)
throws Exception{
System.out.println("拦截开始......");
String result = action.invoke();
System.out.println("拦截结束......");
return result;
}
}
//引用该拦截器并指定不需要拦截的方法
<action name="test" class="MyPackage.TestAction">
<interceptor-ref name="test">
<param name="excludeMethods">execute</param>
</interceptor-ref>
<result name="success">/index.jsp</result>
</action>
下面我们看运行的结果截图:
显然我们指明了该拦截器不用拦截方法execute,当然结果显示的也是如我们所愿。如果我们修改上述struts.xml中内容:
<action name="test" class="MyPackage.TestAction">
<interceptor-ref name="test">
<param name="includeMethods">execute</param>
</interceptor-ref>
<result name="success">/index.jsp</result>
</action>
我们指定该execute方法是需要被拦截器拦截的,下面运行的结果截图:
当然如果需要指定多个方法需要被拦截或者不用被拦截,可以使用英文逗号隔开这些方法,例如:
<param name="includeMethods">方法一,方法二,方法三</param>
最后还有一点是:如果一个方法既被放在了includeMethods中也被放在了excludeMethods中,那么框架将会选择拦截该方法。
五、有关拦截器机制的其他一些细节
拦截器的执行顺序是按照引用拦截器的顺序决定的,例如我们定义两个拦截器:
<action name="test" class="MyPackage.TestAction">
<interceptor-ref name="test"/>
<interceptor-ref name="test2"/>
<result name="success">/index.jsp</result>
</action>
也就是说第一个拦截器拦截action之后,会调用invoke方法,如果还有其他拦截器则会调用下一个拦截器,一层层嵌套,最后结束最外层的拦截器。
上述实例中我们使用param参数为拦截器类中的includeMethods属性赋值,但是如果是一个拦截器栈中我们有该如何为其中某个具体的拦截器属性赋值呢?
<interceptor-ref name="拦截器栈">
<param name="拦截器一.属性名">属性值</param>
</interceptor-ref>
至此,我们简单了解了有关struts2中拦截器器的相关知识,如需深刻理解还要在具体项目中体会,总结不到之处,望海涵!
详解Struts2拦截器机制的更多相关文章
- 详解Mybatis拦截器(从使用到源码)
详解Mybatis拦截器(从使用到源码) MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. 本文从配置到源码进行分析. 一.拦截器介绍 MyBatis 允许你在 ...
- OkHttp3源码详解(三) 拦截器-RetryAndFollowUpInterceptor
最大恢复追逐次数: ; 处理的业务: 实例化StreamAllocation,初始化一个Socket连接对象,获取到输入/输出流()基于Okio 开启循环,执行下一个调用链(拦截器),等待返回结果(R ...
- OkHttp3源码详解(三) 拦截器
1.构造Demo 首先构造一个简单的异步网络访问Demo: OkHttpClient client = new OkHttpClient(); Request request = new Reques ...
- Struts2拦截器的使用 (详解)
Struts2拦截器的使用 (详解) 如何使用struts2拦截器,或者自定义拦截器.特别注意,在使用拦截器的时候,在Action里面必须最后一定要引用struts2自带的拦截器缺省堆栈default ...
- Struts2拦截器详解
一.Struts2拦截器原理: Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的 拦截器对象,然后串成一个列 ...
- struts2拦截器的实现机制
前言 最近老大让每周写一篇技术性的博客,想想也没啥写,就想着随便拿个以前的项目去研究研究五大框架的底层代码.本人水平有限,有不对的地方还望大家勿喷,指正! 开始之前先了解下strtus2的工作流程: ...
- struts2 拦截器和actioninvocation、PreResultListener
Interceptor说明 Interceptor的接口定义没有什么特别的地方,除了init和destory方法以外,intercept方法是实现整个拦截器机制的核心方法.而它所依赖的参数Act ...
- struts2拦截器interceptor的三种配置方法
1.struts2拦截器interceptor的三种配置方法 方法1. 普通配置法 <struts> <package name="struts2" extend ...
- struts2 拦截器 interceptor
struts2 拦截器详解:http://struts2.group.iteye.com/group/wiki/1397-deep-into-struts2-interceptors
随机推荐
- 不忘初心 --- 重读<<The C Programming Language>>
这篇文章应该发布在好几年前,2011年计算机界大师Dennis Ritchie仙逝,那时对大师的映象还停留在大一刚学编程时:Unix的合作开发者,C语言的发明人.通过网上的纪念文章<<Un ...
- 深拷贝/浅拷贝之Js / AngularJs
var a = [1,2,3,4]; var b = a; b[1] = 8; console.log("a:" + a );//1,8,3,4 consloe.log(" ...
- C#设计模式:责任链模式
设计模式是面向对象编程的基础,是用于指导程序设计.在实际项目开发过程中,并不是一味将设计模式进行套用,也不是功能设计时大量引入设计模式.应该根据具体需求和要求应用适合的设计模式.设计模式是一个老话题了 ...
- 自然梯度(Natural Gradient)
自然梯度(Natural Gradient)
- 如何快速将本地项目托管到到github上?
1,打开你的本地项目文件夹,比如 test-demo: 2,打开github(没有github的要自己注册下), 点击new repository 3,填写项目信息,创建项目 4,复制新建的项目url ...
- 100_remove-duplicates-from-sorted-array
/*@Copyright:LintCode@Author: Monster__li@Problem: http://www.lintcode.com/problem/remove-duplica ...
- C#并行编程--命令式数据并行(Parallel.Invoke)
命令式数据并行 Visual C# 2010和.NETFramework4.0提供了很多令人激动的新特性,这些特性是为应对多核处理器和多处理器的复杂性设计的.然而,因为他们包括了完整的新的特性,开 ...
- 用extjs6.0写一个点击新建窗口的功能
一.写一个按钮 注意id { id: 'ListEdit', text:'编辑', iconCls:'x-fa fa-edit' } 二.写新建的页面 下面我新建的是表单,有几点需要注意的: ① 因为 ...
- MSMQ队列学习记录
微软消息队列-MicroSoft Message Queue(MSMQ) 使用感受:简单. 一.windows安装MSMQ服务 控制面板->控制面板->所有控制面板项->程序和功能- ...
- DirectFB学习笔记四
本篇目的,实现按钮的点击事件捕获,也就是鼠标点击,如果点击在方框范围内,则响应,在方框外,则忽略. 由于鼠标移动和点击都会产生事件,因此,我们可以在鼠标移动的时候记录坐标,在点击时比较坐标是否在方框范 ...