Struts2学习----------拦截器介绍

什么是拦截器

 

拦截器(InterceptorStruts 2的一个强有力的工具,有许多功能都是构建于它之上,如国际化、转换器,校验等。

拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。

说到拦截器有一个东西不能落下——拦截器链Interceptor Chain,在Struts 2中称为拦截器栈Interceptor Stack)。

拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

 

实现原理

 

Struts 2的拦截器实现相对简单。当请求到达Struts 2ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表list),最后一个一个地调用列表中的拦截器。

Struts2学习----------拦截器介绍

拦截器调用序列图

示例演示

 

我们先创建一个简单的拦截器来演示下

 

先创建第一个拦截器FirstInterceptor

  1. package cn.lovepi.chapter05.interceptor;  
  2.   
  3. import com.opensymphony.xwork2.ActionInvocation;  
  4. import com.opensymphony.xwork2.interceptor.Interceptor;  
  5.   
  6. /** 
  7.  * Created by icarus on 2016/7/9. 
  8.  * 拦截器演示 
  9.  */  
  10. public class FirstInterceptor implements Interceptor {  
  11.    private String some;  
  12.     /** 
  13.      * 拦截器销毁方法 
  14.      */  
  15.     @Override  
  16.     public void destroy() {  
  17.   
  18.     }  
  19.   
  20.     /** 
  21.      * 初始化方法,当系统启动的时候拦截器就已经初始化完毕了 
  22.      */  
  23.     @Override  
  24.     public void init() {  
  25.         System.out.println(some+"拦截器初始化完成");  
  26.     }  
  27.   
  28.     /** 
  29.      * 拦截器核心代码 
  30.      * @param actionInvocation 
  31.      * @return 
  32.      * @throws Exception 
  33.      */  
  34.     @Override  
  35.     public String intercept(ActionInvocation actionInvocation) throws Exception {  
  36.         // 让到达拦截器的请求继续前进,访问需要访问的资源,  
  37.         // 也就是放过去的意思,返回值就是经过action的返回字符串  
  38.         // 这个字符串到达struts.xml中的result中去。  
  39.         System.out.println("进入First拦截器");  
  40.         String returnName=actionInvocation.invoke();  
  41.         System.out.println("退出First拦截器");  
  42.         System.out.println("returnName"+returnName);  
  43.         return returnName;  
  44.     }  
  45.   
  46.     public String getSome() {  
  47.         return some;  
  48.     }  
  49.   
  50.     public void setSome(String some) {  
  51.         this.some = some;  
  52.     }  
  53. }  
在文件中配置该拦截器
  1. <package name="chapter05" extends="struts-default">  
  2.          <!--拦截器必须得配置在package包下第一个位置-->  
  3.          <interceptors>  
  4.              <interceptor name="ch05FirstInterceptor" class="cn.lovepi.chapter05.interceptor.FirstInterceptor">  
  5.                  <!--配置属性值-->  
  6.                  <param name="some">icarus</param>  
  7.              </interceptor>  
  8.          </interceptors>  
  9.          <!--拦截器演示Action-->  
  10.          <action name="ch05LoginAction" class="cn.lovepi.chapter05.action.LoginAction">  
  11.              <interceptor-ref name="ch05FirstInterceptor"/>  
  12.              <!--必须配置默认拦截器-->  
  13.              <interceptor-ref name="defaultStack"/>  
  14.              <result name="success">/chapter05/login.jsp</result>  
  15.          </action>  
  16. </package>  

可以看到我们的拦截器必须配置在package包下的第一个位置,使用<interceptors>标签来进行配置,可以配置多个拦截器,使用<interceptor>标签来进行配置。还可以在拦截器中设置其属性的,使用<param>标签来对其属性进行赋值。

在配置好拦截器之后,想要在某个Action中使用拦截器,则使用<interceptor-ref>标签来进行指定。

 

注意:其实系统在执行Action之前都会执行默认的拦截器,也就是说每一个Action标签当中其实都有一句:

  1. <interceptor-ref name="defaultStack"/>  
但是这种情况在你配置了一个自定义的拦截器之后便会改变。自定义的拦截器会将默认的拦截器覆盖掉,这时系统中只有一个自定义拦截器起作用,但是我们知道Struts2当中的许多功能都是基于默认的拦截器实现的,如国际化、转换器,校验等。所以我们在引入了自定义的拦截器之后一定要引入默认的拦截器到Action当中。


接下来我们创建第二个拦截器SecondInterceptor

  1. package cn.lovepi.chapter05.interceptor;  
  2.   
  3. import com.opensymphony.xwork2.ActionInvocation;  
  4. import com.opensymphony.xwork2.interceptor.Interceptor;  
  5.   
  6. /** 
  7.  * Created by icarus on 2016/7/9. 
  8.  * 拦截器演示 
  9.  */  
  10. public class SecondInterceptor implements Interceptor {  
  11.     /** 
  12.      * 拦截器销毁方法 
  13.      */  
  14.     @Override  
  15.     public void destroy() {  
  16.   
  17.     }  
  18.   
  19.     /** 
  20.      * 初始化方法,当系统启动的时候拦截器就已经初始化完毕了 
  21.      */  
  22.     @Override  
  23.     public void init() {  
  24.         System.out.println("Second拦截器初始化完成");  
  25.     }  
  26.   
  27.     /** 
  28.      * 拦截器核心代码 
  29.      * @param actionInvocation 
  30.      * @return 
  31.      * @throws Exception 
  32.      */  
  33.     @Override  
  34.     public String intercept(ActionInvocation actionInvocation) throws Exception {  
  35.         // 让到达拦截器的请求继续前进,访问需要访问的资源,  
  36.         // 也就是放过去的意思,返回值就是经过action的返回字符串  
  37.         // 这个字符串到达struts.xml中的result中去。  
  38.         System.out.println("进入Second拦截器");  
  39.         String returnName=actionInvocation.invoke();  
  40.         System.out.println("退出Second拦截器");  
  41.         return returnName;  
  42.     }  
  43. }  
接下来在项目中配置第二个拦截器

  1. <package name="chapter05" extends="struts-default">  
  2.          <!--拦截器必须得配置在package包下第一个位置-->  
  3.          <interceptors>  
  4.              <interceptor name="ch05FirstInterceptor" class="cn.lovepi.chapter05.interceptor.FirstInterceptor">  
  5.                  <!--配置属性值-->  
  6.                  <param name="some">icarus</param>  
  7.              </interceptor>  
  8.              <!--配置第二个拦截器,没有相关属性就需要使用param了-->  
  9.              <interceptor name="ch05SecondInterceptor" class="cn.lovepi.chapter05.interceptor.SecondInterceptor"/>  
  10.          </interceptors>  
  11.          <!--拦截器演示Action-->  
  12.          <action name="ch05LoginAction" class="cn.lovepi.chapter05.action.LoginAction">  
  13.              <interceptor-ref name="ch05FirstInterceptor"/>  
  14.              <interceptor-ref name="ch05SecondInterceptor"/>  
  15.              <!--必须配置默认拦截器-->  
  16.              <interceptor-ref name="defaultStack"/>  
  17.              <result name="success">/chapter05/login.jsp</result>  
  18.          </action>  
  19. </package>  


执行项目,会发现输出的结果是:

Struts2学习----------拦截器介绍

Struts2学习----------拦截器介绍

这里便引出了拦截器当中一个非常重要的概念------拦截器栈

拦截器栈的执行顺序可以抽象成以下模式:

Struts2学习----------拦截器介绍

对应的拦截器栈在Struts.xml文件当中也可以使用<interceptor-stack>标签来表示

那么上面那个示例的配置文件就可变为下面的形式:

  1. <package name="chapter05" extends="struts-default">  
  2.          <!--拦截器必须得配置在package包下第一个位置-->  
  3.          <interceptors>  
  4.              <interceptor name="ch05FirstInterceptor" class="cn.lovepi.chapter05.interceptor.FirstInterceptor">  
  5.                  <!--配置属性值-->  
  6.                  <param name="some">icarus</param>  
  7.              </interceptor>  
  8.              <!--配置第二个拦截器,没有相关属性就需要使用param了-->  
  9.              <interceptor name="ch05SecondInterceptor" class="cn.lovepi.chapter05.interceptor.SecondInterceptor"/>  
  10.              <!--拦截器栈配置-->  
  11.              <interceptor-stack name="cho5AllInterceptor" >  
  12.                  <interceptor-ref name="ch05FirstInterceptor"/>  
  13.                  <interceptor-ref name="ch05SecondInterceptor"/>  
  14.                  <!--必须设置默认拦截器-->  
  15.                  <interceptor-ref name="defaultStack"/>  
  16.              </interceptor-stack>  
  17.          </interceptors>  
  18.          <!--拦截器演示Action-->  
  19.          <action name="ch05LoginAction" class="cn.lovepi.chapter05.action.LoginAction">  
  20.              <!--只需引入一个拦截器即可-->  
  21.              <interceptor-ref name="cho5AllInterceptor"/>  
  22.              <result name="success">/chapter05/login.jsp</result>  
  23.          </action>  
  24. </package>  

在这里便可以引入一个新的概念,全局拦截器全局result

  1. <!--系统默认拦截器,对这个包中的所有action都适用-->  
  2. <default-interceptor-ref name="cho5AllInterceptor"/>  
  3. <!--全局的返回result,当包中任何一个action方法返回error都会去error界面-->  
  4. <global-results>  
  5.      <result name="error">/error.jsp</result>  
  6. </global-results>  

还有一些其他的全局设置参数,因为不长使用便不在介绍了,可以自行了解。

 

我们目前所设置的Action都是直接使用一个类来实现ActionSupport接口,并重写接口默认的execute方法来实现的。所有的逻辑代码全在一个方法中,这对于一个类来说其实是有些浪费的,假如我们需要实现一个功能当中的增删改查方法的话岂不是要编写四个类…….

其实我们是可以在Action类中设置自己的方法的,只需在配置文件中配置到对应的方法即可。

 

如下我们在一个继承了ActionSupport类中编写了adddeleteupdateshow四个方法

  1. package cn.lovepi.chapter05.action;  
  2.   
  3. import com.opensymphony.xwork2.ActionSupport;  
  4.   
  5. /** 
  6.  * Created by icarus on 2016/7/9. 
  7.  * 通配符引入示例 
  8.  */  
  9. public class UsersAction extends ActionSupport{  
  10. //    /**  
  11. //     * 默认的execute方法  
  12. //     * @return  
  13. //     * @throws Exception  
  14. //     */  
  15. //    @Override  
  16. //    public String execute() throws Exception {  
  17. //        System.out.println("进入execute方法");  
  18. //        return SUCCESS;  
  19. //    }  
  20.     /* 
  21.     但是对于一个类来说,只有一个执行方法是有些浪费的。 
  22.     我们可以自定义一些方法,然后在struts.xml文件中配置相应的方法 
  23.      */  
  24.     public String add() throws Exception{  
  25.         System.out.println("进入add方法");  
  26.         return "add_success";  
  27.     }  
  28.     public String show() throws Exception{  
  29.         System.out.println("进入show方法");  
  30.         return "show_success";  
  31.     }  
  32.     public String update() throws Exception{  
  33.         System.out.println("进入update方法");  
  34.         return "update_success";  
  35.     }  
  36.     public String delete() throws Exception{  
  37.         System.out.println("进入delete方法");  
  38.         return "delete_success";  
  39.     }  
  40. }  
我们可以在类中对每个方法的Action进行配置
  1.  <action name="chapter05UsersActionAdd" class="cn.lovepi.chapter05.action.UsersAction" method="add">  
  2.      <result name="success">/chapter05/index.jsp</result>  
  3. </action>  
  4. <action name="chapter05UsersActionShow" class="cn.lovepi.chapter05.action.UsersAction" method="show">  
  5.      <result name="success">/chapter05/index.jsp</result>  
  6. </action>  
  7. <action name="chapter05UsersActionUpdate" class="cn.lovepi.chapter05.action.UsersAction" method="update">  
  8.      <result name="success">/chapter05/index.jsp</result>  
  9. </action>  
  10. <action name="chapter05UsersActionDelete" class="cn.lovepi.chapter05.action.UsersAction" method="delete">  
  11.      <result name="success">/chapter05/index.jsp</result>  
  12. </action>  

但是这样配置起来过于繁琐

 

接下来我们使用通配符来进行配置:

通配符可用于代替字符。通常地,星号“*”匹配0个或以上的字符,问号“?”匹配1个字符。

 

这样的话,上面的那些action可以用如下的代码进行配置:

  1. <!--使用通配符方法配置,说白了就是在数星星,第一个星星的内容用{1}来代表-->  
  2. <action name="users_*" class="cn.lovepi.chapter05.action.UsersAction" method="{1}">  
  3.        <interceptor-ref name="ch05MethodInterceptor"/>  
  4.        <!--必须配置默认的拦截器-->  
  5.        <interceptor-ref name="defaultStack"/>  
  6.        <result name="{1}_success">/chapter05/{1}index.jsp</result>  
  7. </action>  

在这里我们是对方法进行拦截器,所有我们得使用对应的方法拦截器

方法拦截器和普通的拦截器有所不同,普通的拦截器是实现Interceptor接口而方法拦截器是继承自MethodFilterInterceptor

  1. package cn.lovepi.chapter05.interceptor;  
  2.   
  3. import com.opensymphony.xwork2.ActionInvocation;  
  4. import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;  
  5.   
  6. /** 
  7.  * Created by icarus on 2016/7/10. 
  8.  * 方法拦截器,只针对对应的方法来进行拦截 
  9.  */  
  10. public class MethodInterceptor extends MethodFilterInterceptor{  
  11.     @Override  
  12.     protected String doIntercept(ActionInvocation actionInvocation) throws Exception {  
  13.         System.out.println("进入方法拦截器");  
  14.         String returnName=actionInvocation.invoke();  
  15.         System.out.println("退出方法拦截器");  
  16.         return returnName;  
  17.     }  
  18. }  

这实际开发中,正确使用通配符来编写配置文件可以大大提高我们的开发效率同时也可以规范我们的代码。

  1. <!--实际项目开发中所使用的通配符配置,第一个*是类名,第二个*是类中对应的方法  
  2. 在表单的action中也要按照这种方式来编写  
  3. 其实,使用通配符来配置同时也是一种编码规范-->  
  4. <action name="*_*" class="cn.lovepi.chapter05.action.{1}Action" method="{2}">  
  5.    <result name="{1}_{2}_success">/chapter05/{1}{2}index.jsp</result>  
  6. </action>