struts2学习笔记之拦截器(Interceptor)

时间:2022-12-26 10:25:56
一、拦截器介绍
拦截器即interceptor
其实现类似于Servlet技术中的Filter,与AOP概念是相关的。
关于AOP可以参考:
http://www.cnblogs.com/wayfarer/articles/241024.html
http://baike.baidu.com/view/73626.htm

Servlet中的Filter技术可以参考:
http://blog.csdn.net/sun305355024sun/article/details/4815471
http://download.oracle.com/javaee/6/api/javax/servlet/Filter.html

拦截器通常封装了一些具有独立逻辑的通用功能,比如登录校验、异常捕捉、日志记录等等;利用它
可以在Action的执行前后做一些额外的工作,比如在执行前进行用户登录信息检查,若未登录跳转到登录页面,
否则执行Action。

二、拦截器的接口与实现
[java] view plaincopy
  1. public interface Interceptor extends Serializable{  
  2.     void destroy();  // 销毁时执行,用于释放在init方法使用的资源  
  3.     void void init(); //初始化时执行  
  4.   
  5.   
  6.     //拦截方法的实现  
  7.     String intercept(ActionInvocation invocation)throws Exception;  
  8. }  


AbstractInterceptor实现了该接口,并提供init和destroy方法的空实现,一般情况下实现自定义拦截器只需要
继承该类并重写intercept方法即可。


intercept方法的实现示例(登录信息检查):
[java] view plaincopy
  1. ActionContext  context = invocation.getInvocationContext();  
  2.    Map session = context.getSession();  
  3.    String user = session.get("user");  
  4.    if(user == null){     
  5.        return Action.LOGIN;    //未登录,直接返回登录逻辑视图  
  6.    }else{  
  7.        return invocation.invoke();  //已登录则继续执行  
  8.     }  

三、拦截器的配置

1 在package中定义拦截器

[html] view plaincopy
  1. <interceptors>  
  2.         <interceptor name="exceptionManager"  class="cn.uc.mms.action.ExceptionInterceptor"/>  
  3.  </interceptors>  

2 拦截器可以组成拦截器栈,拦截器栈可以由拦截器和栈组成。

[html] view plaincopy
  1. <interceptor-stack  name="myDefault">    
  2.         <interceptor-ref name="exceptionManager"/>  //引用前面定义的拦截器  
  3.         <interceptor-ref  name="defaultStack"/>    //引用当前上下文的默认拦截器栈  
  4.    </interceptor-stack>    


  3 可以为package定义全局的拦截器引用,该定义仅支持一个引用,如果需要引用多个拦截器,可以声明为一个拦截器栈。
    <default-interceptor-ref name="myDefault" />    //该声明需要在拦截器或拦截器栈定义之后,Action声明之前
    
  4 为Action定义拦截器引用:
     <interceptor-ref name="exceptionManager"></interceptor-ref>  
       需要注意的是Action定义了拦截器引用之后,全局的拦截器引用将失效,需要显示声明来恢复:
     <interceptor-ref name="myDefault"></interceptor-ref>

  5 为拦截器注入参数:

      interceptor/interceptor-stack、interceptor-ref和default-interceptor-ref都可以通过param向interceptor注入参数

      简单情况如:

      <param name="name">拦截器A</param>     //拦截器需要有name属性(定义了setName方法)即可注入。

      但如果是从interceptor-stack进行参数注入,则param的name属性需要包含目标拦截器的名称前缀:

       <param name="interceptorA.name">拦截器A</param>
       如果同一个拦截器的param定义出现冲突,则取最后定义的值。
 

 6 拦截器的执行顺序:

      把拦截器想象成一个栈,由配置顺序构造出一个拦截器的执行列表,最底下是Action;

      从第一个拦截器往下执行,执行完Action之后又从最后一个拦截器往上走。

四、实现常用的拦截功能
     1 过滤方法的拦截器: MethodFilterInterceptor,指定excludeMethods黑名单、includeMethods为白名单,多个方法以,分隔。
                                               如果一个方法在两个名单中,那么会被拦截(被黑)
        使用方式:用一个类继承该类,并进行配置即可。
  
     2 监听Action返回的逻辑结果

       实现PreResultListener的接口,并在action执行前为invocation对象添加监听;

      通过拦截器实现上述动作:
[java] view plaincopy
  1. public class MyPreResultListener implements PreResultListener{  
  2.           public void beforeResult(ActionInvocation invocation, String resultCode){  
  3.                ...  
  4.                //resultCode为action返回的逻辑视图名  
  5.            }  
  6.        }  
     使用拦截器在action执行前执行:
[java] view plaincopy
  1. invocation.addPreResultListener(new MyPreResultListener());  
  2. <span style="color:#FF0000;">//注意beforeResult中不可再执行invocation.invoke(小心死循环)</span>  

    
五、最佳实践-- 实现全局异常捕获的拦截器
    实现一个拦截器,捕获Action执行时发生的异常,将结果信息写入到ActionContext的值栈中,并返回ERROR视图:  
     拦截器实现:
[java] view plaincopy
  1. public class ExceptionInterceptor extends AbstractInterceptor {  
  2.     private static final Log log = LogFactory.getLog(ExceptionInterceptor.class);  
  3.   
  4.   
  5.     @Override  
  6.     public String intercept(ActionInvocation invocation) throws Exception {  
  7.         String actionName = invocation.getInvocationContext().getName();  
  8.         try {  
  9.                 /*以下代码用于检测在该拦截器之前是否有其他的拦截器执行了某些校验且不通过的情况, 
  10.                  *比如文件上传的条件检查、表单校验之类,若有则一并当做异常进行捕获返回。 
  11.                  *该做法不是必须的,因需求而异。 
  12.                  */  
  13.             Object action = invocation.getAction();  
  14.             if (action instanceof ValidationAware) {  
  15.                 ValidationAware validation = (ValidationAware) action;  
  16.                 if(validation.hasFieldErrors()){  
  17.                     String errorStr = JSONObject.fromObject(validation.getFieldErrors()).toString();  
  18.                     throw new RuntimeException("拦截到错误:" + errorStr);  
  19.                 }  
  20.             }  
  21.                    
  22.               return invocation.invoke();  
  23.         } catch (Exception ex) {  
  24.             ex.printStackTrace();  
  25.             log.error("Error by action{" + actionName + "}", ex);  
  26.             HashMap<String, String> map = new HashMap<String, String>();  
  27.             String msg = "unknown";  
  28.             if(ex.getMessage() != null){  
  29.                 msg = ex.getMessage();  
  30.             }  
  31.             map.put("success", String.valueOf(false));  
  32.             map.put("error", msg);  
  33.             map.put("errorType", ex.getClass().toString());  
  34.               
  35.             ActionContext ac = invocation.getInvocationContext();  
  36.             ValueStack stack = ac.getValueStack();  
  37.             stack.set("error", map);  //将错误信息注入到ValueStack中  
  38.             return "error";  
  39.       }  
  40.     }  
  41.      }  
   
     配置文件:
[html] view plaincopy
  1. <interceptors>  
  2.    <interceptor name="exceptionManager"  class="action.ExceptionInterceptor"/>  
  3.    <interceptor-stack   name="myDefault">    
  4.          <interceptor-ref name="exceptionManager"/>   <!--将其配置在所有拦截器之上 -->  
  5.          <interceptor-ref   name="defaultStack"/>    
  6.    </interceptor-stack>    
  7. </interceptors>   
  8. <default-interceptor-ref name="myDefault" />  
  9.   
  10. <global-results>   <!-- 配置全局逻辑结果 -->  
  11. result name="error" type="json">  
  12.  <param name="root">error</param>  
  13.         <param name="contentType">text/html;charset=UTF-8</param>  
  14.         <param name="excludeNullProperties">true</param>   
  15.    </result>  
  16.  </global-results>  
  17.   
  18.   
  19. <action name="upload" class="..." method="doUpload">    
  20.       <interceptor-ref name="fileUpload">    
  21.          <param name="allowedTypes">application/vnd.ms-excel,application/excel,application/octet-stream</param>  
[html] view plaincopy
  1.         <param name="maximumSize">8192000</param>    
  2.       </interceptor-ref>    
  3.       <interceptor-ref name="myDefault"></interceptor-ref>  <!-- 文件上传的拦截,内置支持..详见代码 -->  
  4.  </action>    
  5. <action name="*" class="action.{1}Action"></action>  

六、struts内置拦截器
     struts的拦截器是灵活的可插拔的,其通过强大的内置拦截器栈来完成大量通用的功能。
    比如params拦截器将http请求参数自动注入到action的属性中;validation完成表单校验;fileUpload用于
文件上传中将文件域信息注入到action的指定属性中,同时也提供相关的条件检查(如文件大小、类型)

    然而尽管内置拦截器栈是强大的,但有时也会导致一些不必要的开销,比如:
fileUpload的拦截器在默认拦截器栈中有一个,在普通用法中常常是另外声明一个fileUpload拦截器置于默认栈之前。
如此,在默认栈中的fileUpload拦截器则是多余。此时要提高性能则需自行定制。

     struts内置拦截器列表及说明:
     http://tsunzhang.iteye.com/blog/811565