Struts2拦截器(一)

来源:互联网 发布:魔龙诀护盾进阶数据 编辑:程序博客网 时间:2024/05/02 00:12

一、 理解拦截器

拦截器可以动态地拦截发送到指定 Action 的请求,可以在 Action 执行的前后插入某些代码。

通过这种方式,就可以把多个 Action 中需要重复指定的代码提取出来,放在拦截器里定义,从而提供更好的代码重用性。

 

1.1 理解 DRY 规则

  在软件开发中,有一条非常重要的规则: don't repeat yourself,就是所谓 DRY 规则,意思是不要书写重复代码

   在软件开发阶段,因为前期设计的不合理,或者缺乏预见性等原因,可能导致系统的多个地方需要使用相同的代码段。但对于许多刚开始从事软件开发的人而言,包含相同的代码段并不会给他们引起任何麻烦,一路复制粘帖。但如果有一天需要改程序中那些‘相同代码’时,有 1000 个地方,就要改 1000 个地方,工作量巨大。

   在这种情况下,一般会把‘相同的代码’定义成一个方法,直接调用这个方法,如果需要修改,只需要该一个方法就行了,这样大大降低了软件后期维护的复杂度。

 

1.2 拦截器的意义

 

 上面已经介绍了,把重复代码放在一个方法内(这里称为 方法1),这样程序要修改只要修改这个方法。但是这样也产生了另一个隐藏问题: 如果要弃用方法1,改用方法2,一样有 1000 个地方,就需要改 1000 个地方,造成这种结果是因为以硬编码方式调用了方法1。

 

  Struts 2 的拦截器就可实现这种需求: 称某个实例是一个拦截器时,这是就其行为上而言: 从代码角度来看,拦截器就是一个类,这个类也包含方法,只是这个方法是个特殊方法,它会在目标方法调用之前 ”自动“ 执行

 

  对比直接调用方法与拦截器的关键区别在于 :  如果不使用拦截器,代码中需要显式通过代码来调用目标方法,如果使用拦截器,则该方法的调用是由系统完成的。

 

  通过以上对比,发现拦截器的优势: 拦截器提供了更高层次的解耦,目标代码无需手动调用目标方法,而是由系统完成,从而将这种调用从代码层次上升到更高层次,从而提供了更高层次的解耦。

提示 : 计算机本身是无法 “自动” 调用拦截器方法,它甚至无法知道到底是应该调用拦截器方法一,还是调用拦截器方法二。对 Struts 2 框架而言,程序通过配置文件来管理拦截器,从而让系统明白: 何时应该调用哪个拦截器。

1.3 拦截器的实现原理

DogDAO

package com.wst.dao;public interface DogDAO {//run方法声明public  void run();//info方法声明public void info();}


DogDAOImpl

package com.wst.dao.impl;import com.wst.dao.DogDAO;public class DogDAOImpl implements DogDAO {public void info() {System.out.println("我是一只狗");}public void run() {System.out.println("奔跑");}}

DogIntercepter

package com.wst.Intercepter;public class DogIntercepter {public void method1(){System.out.println("模拟方法1");}public void method2(){System.out.println("模拟方法2");}}

ProxyHandler

package com.wst.Intercepter;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class ProxyHandler implements InvocationHandler {//需要代理的目标对象private Object target;//用于设置传入目标对象的方法 public void setTarget(Object o) {          this.target = o;  }  //创建拦截器实例DogIntercepter intercepter = new DogIntercepter();//执行代理的目标方法是,该invoke方法会自动执行public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Object result = null;//如果被调用的方法的方法名是 infoif(method.getName().equals("info")){//调用拦截器方法1intercepter.method1();result = method.invoke(target, args);}else{result = method.invoke(target, args);}return result;}

MyProxyFactory

package com.wst.Intercepter;import java.lang.reflect.Proxy;public class MyProxyFactory {public static Object getTarget(Object object){//代理的处理类ProxyHandler handler = new ProxyHandler();//讲实例交给代理操作handler.setTarget(object);// 第一个参数是用来创建动态代理的ClassLoader对象,  // 只要该对象能访问Dog接口即可  // 第二个参数是接口数组,正是代理该接口数组  // 第三个参数是代理包含的处理实例  return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), handler);}}

Test

package com.wst.test;import com.wst.Intercepter.MyProxyFactory;import com.wst.dao.DogDAO;import com.wst.dao.impl.DogDAOImpl;public class Test {/** * @param args */public static void main(String[] args) {// 创建一个Dog实例,该实例将被作为代理的目标对象 DogDAO targetDog = new DogDAOImpl();DogDAO dog = null;// 以目标对象创建代理  Object proxy = MyProxyFactory.getTarget(targetDog);if(proxy instanceof DogDAO){dog = (DogDAO)proxy;}// 测试代理的方法  dog.info();}}

1.4 拦截器和 AOP的关系

 拦截器与 AOP (Aspect Orient Program ,面向切面编程 ) 是密切相关的, AOP 从程序运行角度来考虑程序的流程,取得业务处理过程的切面,在特定切面通过系统自动插入特定方法。 AOP 面向的是程序运行中各个步骤,以一种松耦合方式来组合业务处理的各个步骤。

AOP 3 个重要概念:

 ● 目标对象 : 包含被拦截方法的原始对象。

 ● 被插入的处理方法 : 定义在拦截器中,会在被拦截方法之前、之后自动执行的方法

 ● 代理对象 : 以目标对象为蓝本,由系统创建的新对象


在上面 3 个概念中,被插入的处理方法不能独立存在,因为方法必须有一个存在的载体:这个载体就是拦截器,拦截器就是包含处理方法的特殊实例

代理对象也称为 AOP 代理,就是由系统动态生成的一个对象,该对象将代替目标对象来使用, AOP 代理包含了目标对象的全部方法。 但 AOP 代理中的方法与目标对象的方法存在差异: AOP 方法在特定切面插入拦截器方法,在处理之间回调目标对象的方法,从而实现了在执行目标方法之前或者之后调用拦截器方法,仿佛拦截器拦截了原有的目标方法一样。

1.5 拦截器在 Struts 2 中的作用


对于任何 MVC 框架来说,他们都会完成一些通用的控制逻辑: 例如解析请求参数,类型转换,将请求参数封装成 DTO (Data Transfer Object),执行输入校验,解析文件上传表单中的文件域,防止表单多次提交……这些操作又不是所有 Action 都需要实现的,所以需要动态的方式来自由组合。

 Struts 1 把这些动作写死在核心控制器里,这样有 2 个缺点:

 ● 灵活性差: 强制所有项目都必须使用该框架提供的全部功能,不管用户是否需要,核心控制器总是会完成这些操作

 ● 可扩展性差: 如果用户需要让核心控制器完成更多自定义的处理,这就比较困难了。在 Struts 1 时代都是通过扩展 Struts 1 的核心控制器来实现的。

Struts 2 改变了这种做法,它把大部分核心控制器需要完成的工作按功能分开定义,每个拦截器完成一个功能。而这些拦截器可以自由选择,灵活组合(甚至不用 Struts 2 的任何拦截器),开发者需要使用哪些拦截器,只需要在 struts.xml 文件中指定使用该拦截器即可。

拦截器的用法非常灵活,Struts 2 允许将完成各种 “小功能” 的方法定义成小粒度的拦截器,如果有一些拦截器经常需要固定在一起使用,又可以将这批小粒度的拦截器定义成大粒度的拦截器栈。从结构上来看,拦截器栈相当于多个拦截器的组合;从功能上来看,拦截器栈也是拦截器,只是一个功能更强大的拦截器

提示 : 一个拦截器栈可以组合多个小粒度的拦截器,一个小粒度的拦截器可以被多个拦截器栈组合。通过组合不同的拦截器,我们能以自己需要的方式来组合 Struts 2 框架的各种功能;通过扩展自己的拦截器,我们可以 “无限” 扩展 Struts 2 框架。


Struts 2 框架的绝大部分功能都是通过拦截器完成的,当 FilterDispatcher 拦截到用户请求之后,大量拦截器将会对用户请求进行处理,然后才会调用用户的开发的 Action 实例的方法来处理请求。

拦截器与 Action 的关系:

 ① SerlvetDispatch 初始化一个 ActionProxy 实例,并调用它的 execute 方法

 ② 拦截器方法会先拦截、并处理用户请求,然后才到 Action 的 execute 方法处理用户请求

 ③ 返回一个逻辑视图名,系统负责将该逻辑视图对应的资源显示给用户

对于 Struts 2 框架而言,有些通用功能对于所有的 Action 都是必须的,所以把这些拦截器组合成拦截器栈(Intercepter Stack), 并将其配置成默认的拦截器应用。

因为拦截器是通过配置文件指定的,因此通过拦截器来引入通用操作的方式,完全是可插拔式的,当系统需要执行这些通用操作时,则配置文件引入这些拦截器即可;如果系统不需要执行这些通用操作,则在配置文件中取消引入即可。


注意 :Struts 2 已经默认启用了大量通用功能的拦截器,只要我们配置 Action 的 package 继承了 struts-default 包,这些拦截器就会起作用。可以到 struts2-core-2.2.jar 里看 struts-default.xml 文件,在该文件内可以看到大量拦截器、拦截器栈


二、拦截器配置初步

在 struts.xml 文件中定义拦截器只需为拦截器类指定一个拦截器名,就完成了拦截器定义。如下:

    <!-- 通过指定拦截器名和拦截器实现类来定义拦截器 -->      <interceptor name="拦截器名" class="拦截器实现类" />  

如果需要在配置拦截器时传入参数,则如下:

<interceptor name="拦截器名" class="拦截器实现类" >      <param name="参数名">参数值</param>  </interceptor>  

还可以把多个拦截器连在一起组成拦截器栈

完全可以认为拦截器栈是一个更大的拦截器,如下:

<interceptor-stack name="拦截器栈名">      <interceptor-ref name="拦截器一" />      <interceptor-ref name="拦截器二" />  </interceptor-stack> 
注意 : 有 2 个时机为拦截器指定参数值,一个是当定义拦截器 (通过 <intercepter .../> 来定义拦截器)时指定拦截器的参数值;另一个是当使用拦截器 (通过 <interceptor-ref .../> 使用拦截器)时指定拦截器的参数值。前者指定的是拦截器参数的默认值。如果在两个时机为同一个参数指定了不同的参数值,则使用拦截器时指定的参数值将会覆盖默认的参数值。

2.2  使用拦截器

 一旦定义了拦截器和拦截器栈后,就可以使用这个拦截器或拦截器栈来拦截 Action 了,拦截器、拦截器栈的拦截行为将会在 Action 的 execute() 方法执行之前被执行

 通过 <interceptor-ref .../> 元素可以在 Action 内使用拦截器:

    <interceptors>              <!-- 配置mysimple拦截器 -->          <interceptor name="mysimple" class="lee.SimpleInterceptor">              <!-- 为拦截器指定参数值 -->              <param name="name">简单拦截器</param>          </interceptor>      </interceptors>       <action name="login" class="lee.LoginAction">          <result name="error">/error.jsp</result>          <result name="success">/welcome.jsp</result>           <!-- 拦截器一般配置在result元素之后! -->          <!-- 配置系统的默认拦截器 -->          <interceptor-ref name="defaultStack"/>          <!-- 应用自定义的mysimple拦截器 -->          <interceptor-ref name="mysimple">              <param name="name">改名后的拦截器</param>          </interceptor-ref>      </action>  

在执行 LoginAction 之前,拦截器会起作用 


2.3  配置默认拦截器

   当配置一个包时,可以为其指定默认拦截器。一旦为某个包指定了默认的拦截器,如果该包中的 Action 没有显示指定拦截器,则默认的拦截器将会起作用。但值得注意的是 : 一旦我们为该包中的 Action 显示应用了某个拦截器,则默认的拦截器就不会起作用,如果该 Action 需要使用该默认拦截器,则必须手动配置该拦截器的引用

 

注意 : 只有当 Action 中没有显示应用拦截器时,该 Action 所在包的默认拦截器才会生效。

 

配置默认拦截器使用 <default-interceptor-ref .../> 元素,该元素作为 <package .../> 元素的子元素使用,为该包下的所有 Action 配置默认的拦截器

 

配置 <default-interceptor-ref .../> 元素时,需要指定一个 name 属性,该 name 属性值是一个已经存在拦截器(栈)的名字,表明将该拦截器(栈)配置成该包的默认拦截器。需要注意的是 : 每个 <package .../> 元素只能有一个<default-interceptor-ref .../> 子元素,即每个包只能指定一个默认拦截器

    <package name="包名">          <interceptors>              <!-- 定义拦截器 -->              <interceptor></interceptor>              <!-- 定义拦截器栈 -->              <interceptor-stack ></interceptor-stack>          </interceptors>          <!-- 配置该包下的默认拦截器(栈) -->          <default-interceptor-ref name="拦截器(栈)名">              <!-- 在配置默认拦截器时为该拦截器指定参数值  -->              <param name="参数名">参数值</param>          </default-interceptor-ref>          <!--配置 多个  Action  -->          <action></action>      </package>  

注意: 每个包只能指定一个默认拦截器。如果要指定多个拦截器,可以把多个拦截器定义成拦截器栈,然后把这个拦截器栈配置成默认拦截器即可。

三、Struts 2 内建的拦截器

   从 Struts 2 框架来看,拦截器几乎完成了70% 的工作,包括解析请求参数、将请求参数赋值给 Action 属性、执行数据校验、文件上传……, Struts 2 设计的灵巧性,更大程度地得益于拦截器设计,当需要扩展 Struts 2 功能时,只需要提供对应拦截器,并将它配置在 Struts 2 容器中即可;如果不需要该功能,也只需要取消该拦截器的配置即可。(可插拔式设计)

 

3.1  Struts 2 内建的拦截器

  Struts 2 内建了大量拦截器,这些拦截器以 name-class 对的形式配置在 struts-default.xml 中,如果我们定义的 package 继承了 Struts 2 的默认 struts-default 包,则可以自由使用下面定义的拦截器,否则必须自己定义这些拦截器

 

下面是 Struts 2 内建拦截器的简要介绍:

 ● alias : 实现在不同请求中相似参数别名的转换

 ● autowiring : 自动装配的拦截器,主要用于当 Struts 2 和 Spring 整合时,Struts 2可以使用自动装配的方式来访问 Spring 容器中的 Bean

 ● chain : 构建一个 Action 链,使当前 Action 可以访问前一个 Action 的属性,一般和 <result type="chain" .../> 一起使用

 ● conversionError : 这是一个负责处理类型转换错误的拦截器,它负责将类型转换错误从 ActionContext 中取出,并转换成 Action 的 FieldError 错误

 ● createSession : 负责创建一个 HttpSession 对象,主要用于那些需要有 HttpSession 对象才能正常工作的拦截器中。

 ● debugging : 当使用 Struts 2 的开发模式时,这个拦截器会提供更多的调试信息

 ● execAndWait : 后台执行 Action, 负责将等待画面发送给用户

 ● exception : 负责处理异常,将异常映射为结果

 ● fileUpload : 用于文件上传,负责解析表单中文件域的内容

 ● i18n : 支持国际化的拦截器,负责把所选的语言、区域放入用户 Session 中

 ● logger : 负责日志记录的拦截器,主要是输出 Action 名字

 ● modelDriven : 用于模型驱动的拦截器,当某个 Action 类实现了 ModelDriven 接口时,负责把 getModel() 方法的结果放入 ValueStack 中

 ● scopedModelDriven : 如果一个 Action 实现了一个 ScopedModelDriven 接口,该拦截器负责从指定生存范围中找出指定的 Model ,并将通过 setModel 方法将该 Model 传给 Action 实例

 ● params : 负责解析 HTTP 请求中的参数,并将参数值设置成 Action 对应的属性值

 ● prepare : 如果 action 实现了 Preparabel 接口,将会调用该拦截器的 prepare() 方法

 ● staticParams : 负责将 xml 中 <action> 标签下 <param> 标签中的参数值传入 action

 ● scope : 范围转换拦截器,可以将 Action 状态的信息保存到 HttpSession 范围,或者保存到 ServletContext 范围内

 ● servletConfig : 如果某个 Action 需要直接访问 Servlet API ,就是通过这个拦截器实现的

 ● timer : 负责输出 Action 的执行时间,这个拦截器在分析该 Action 的性能瓶颈时比较有用。

 ● token : 用于阻止重复提交,它检查传到 Action 中的 token,从而防止多次提交

 ● tokenSession : 这个拦截器的作用与前一个基本类似,只是它把 token 保存在 HttpSession 中

 ● validation : 通过执行在 xxxAction-validation.xml 中定义的校验器,从而完成数据校验

 ● workflow : 负责调用 Action 类中的 validate 方法,如果校验失败,则返回 input 逻辑视图

 ● store : 负责将 Action 的 messages、errors 、fieldErrors 保存到 session,也负责从 session 中读取 Action 的 messages、errors、fieldErrors

 ● checkbox : 负责检测那些没有勾选的复选项,为它们添加默认值(false)

 ● roles : 是一个 JAAS ( Java Authentication and Authorization Service, Java 授权和认证服务 ) 拦截器,只有当浏览者取得合适的授权后,才可以调用该拦截器拦截的 Action

 ● annotationWorkflow : 这是支持 Struts 2 “零配件” 特性的拦截器

 

大部分时候,开发者无需手动控制这些拦截器,因为 struts-default.xml 文件中已经配置了这些拦截器,只要我们定义的包继承了系统的 struts-default 包,就可以直接使用这些拦截器

 

3.2  struts-default.xml 里配置的拦截器

 

struts-default.xml 文件是 Struts 2 默认的配置文件,不管什么时候,肯定会加载

一般情况下,直接使用系统的 defaultStack 拦截器栈即可,虽然它可能会做一些额外的拦截工作,但对系统不会有太大的影响。如果开发者对每个拦截器所做的工作非常熟悉,而且可以灵活地组合必须的拦截器,对 Struts 2 性能优化是有帮助的,但是一般推荐直接使用 defaultStack

 

因为 Struts 2 的  struts-default 包中指定 defaultStack 拦截器栈是默认的拦截器,因此如果用户定义的包继承了 struts-default 包,则也会将 defaultStack 拦截器栈作为默认的拦截器栈。这意味着: 如果系统中的 Action 配置没有指定拦截器引用,系统会将 defaultStack 拦截器栈自动作用于该 Action



[参考博文]
原创粉丝点击