SpringMVC 攔截器

原文鏈接:http://jinnianshilongnian.iteye.com/blog/1670856

常見應用場景

1、日誌記錄:記錄請求信息的日誌,以便進行信息監控、信息統計、計算PV(Page View)等。

2、權限檢查:如登錄檢測,進入處理器檢測檢測是否登錄,如果沒有直接返回到登錄頁面;

3、性能監控:有時候系統在某段時間莫名其妙的慢,可以通過攔截器在進入處理器之前記錄開始時間,在處理完後記錄結束時間,從而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄);

4、通用行爲:讀取cookie得到用戶信息並將用戶對象放入請求,從而方便後續流程使用,還有如提取Locale、Theme信息等,只要是多個處理器都需要的即可使用攔截器實現。

5、OpenSessionInView:如Hibernate,在進入處理器打開Session,在完成後關閉Session。

…………本質也是AOP(面向切面編程),也就是說符合橫切關注點的所有功能都可以放入攔截器實現。

攔截器接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package  org.springframework.web.servlet;
     public  interface  HandlerInterceptor {
         boolean  preHandle(
             HttpServletRequest request, HttpServletResponse response, Object handler) 
             throws  Exception;
         void  postHandle(
             HttpServletRequest request, HttpServletResponse response, 
             Object handler, ModelAndView modelAndView) 
             throws  Exception;
         void  afterCompletion(
             HttpServletRequest request, HttpServletResponse response, 
             Object handler, Exception ex)
             throws  Exception;
     } 

preHandle:預處理回調方法,實現處理器的預處理(如登錄檢查),第三個參數爲響應的處理器;

返回值:true表示繼續流程(如調用下一個攔截器或處理器);

            false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;

postHandle:後處理回調方法,實現處理器的後處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能爲null。

afterCompletion:整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中preHandle返回true的攔截器的afterCompletion。

攔截器適配器

有時候我們可能只需要實現三個回調方法中的某一個,如果實現HandlerInterceptor接口的話,三個方法必須實現,不管你需不需要,此時spring提供了一個HandlerInterceptorAdapter適配器(一種適配器設計模式的實現),允許我們只實現需要的回調方法。

?
1
2
3
public  abstract  class  HandlerInterceptorAdapter  implements  HandlerInterceptor {
      //省略代碼 此處所以三個回調方法都是空實現,preHandle返回true。
}

運行流程圖

正常流程:

f1.jpg

中斷流程:

f2.jpg

DispatcherServlet內部到底是如何工作的

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public  abstract  class  HandlerInterceptorAdapter  implements  HandlerInterceptor {
      //省略代碼 此處所以三個回調方法都是空實現,preHandle返回true。
}
     //doDispatch方法
     //1、處理器攔截器的預處理(正序執行)
     HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
     if  (interceptors !=  null ) {
         for  ( int  i =  0 ; i < interceptors.length; i++) {
             HandlerInterceptor interceptor = interceptors[i];
             if  (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler()))            {
                 //1.1、失敗時觸發afterCompletion的調用
                 triggerAfterCompletion(mappedHandler, interceptorIndex, 
                 processedRequest, response,  null );
                 return ;
             }
             interceptorIndex = i; //1.2、記錄當前預處理成功的索引
         }
     }
     //2、處理器適配器調用我們的處理器
     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
     //當我們返回null或沒有返回邏輯視圖名時的默認視圖名翻譯
     if  (mv !=  null  && !mv.hasView()) {
         mv.setViewName(getDefaultViewName(request));
     }
     //3、處理器攔截器的後處理(逆序)
     if  (interceptors !=  null ) {
         for  ( int  i = interceptors.length -  1 ; i >=  0 ; i--) {
           HandlerInterceptor interceptor = interceptors[i];
           interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
         }
     }
     //4、視圖的渲染
     if  (mv !=  null  && !mv.wasCleared()) {
         render(mv, processedRequest, response);
         if  (errorView) {
             WebUtils.clearErrorRequestAttributes(request);
         }
     //5、觸發整個請求處理完畢回調方法afterCompletion
     triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response,  null );

注:以上是流程的簡化代碼,中間省略了部分代碼,不完整。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// triggerAfterCompletion方法
private  void  triggerAfterCompletion(HandlerExecutionChain mappedHandler,  int  interceptorIndex,
HttpServletRequest request, HttpServletResponse response, Exception ex)  throws  Exception {
     // 5、觸發整個請求處理完畢回調方法afterCompletion (逆序從1.2中的預處理成功的索引處的攔截器執行)
     if  (mappedHandler !=  null ) {
         HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
         if  (interceptors !=  null ) {
             for  ( int  i = interceptorIndex; i >=  0 ; i--) {
                 HandlerInterceptor interceptor = interceptors[i];
                 try  {
                     interceptor.afterCompletion(request, response, mappedHandler.
                     getHandler(), ex);
                 }
                 catch  (Throwable ex2) {
                     logger.error( "HandlerInterceptor.afterCompletion threw exception" , ex2);
                 }
             }
         }
     }
}

正常流程

1、攔截器實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package  cn.javass.chapter5.web.interceptor;
//省略import
//此處一般繼承HandlerInterceptorAdapter適配器即可
public  class  HandlerInterceptor1  extends  HandlerInterceptorAdapter {
     @Override
     public  boolean  preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler)  throws  Exception {
         System.out.println( "===========HandlerInterceptor1 preHandle" );
         return  true ;
     }
     @Override
     public  void  postHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler, ModelAndView modelAndView)  throws  Exception {
         System.out.println( "===========HandlerInterceptor1 postHandle" );
     }
     @Override
     public  void  afterCompletion(HttpServletRequest request, HttpServletResponse response, 
     Object handler, Exception ex)  throws  Exception {
         System.out.println( "===========HandlerInterceptor1 afterCompletion" );
     }
}

2、控制器

?
1
2
3
4
5
6
7
8
9
10
package  cn.javass.chapter5.web.controller;
//省略import
public  class  TestController  implements  Controller {
     @Override
     public  ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) 
     throws  Exception {
         System.out.println( "===========TestController" );
         return  new  ModelAndView( "test" );
     }
}

3、Spring配置文件chapter5-servlet.xml

?
1
2
3
4
5
< bean  name = "/test"  class = "cn.javass.chapter5.web.controller.TestController" />
< bean  id = "handlerInterceptor1" 
class = "cn.javass.chapter5.web.interceptor.HandlerInterceptor1" />
< bean  id = "handlerInterceptor2" 
class = "cn.javass.chapter5.web.interceptor.HandlerInterceptor2" />
?
1
2
3
4
5
6
7
8
< bean  class = "org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" >
     < property  name = "interceptors" >
         < list >
            < ref  bean = "handlerInterceptor1" />
            < ref  bean = "handlerInterceptor2" />
         </ list >
     </ property >
</ bean >

    interceptors:指定攔截器鏈,攔截器的執行順序就是此處添加攔截器的順序

4、視圖頁面WEB-INF/jsp/test.jsp

?
1
2
3
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%System.out.println("==========test.jsp");%>
test page

在控制檯輸出 test.jsp

5、啓動服務器測試

輸入網址:http://localhost:9080/springmvc-chapter5/test

控制檯輸出:

===========HandlerInterceptor1 preHandle

===========HandlerInterceptor2 preHandle

===========TestController

===========HandlerInterceptor2 postHandle

===========HandlerInterceptor1 postHandle

==========test.jsp

===========HandlerInterceptor2 afterCompletion

===========HandlerInterceptor1 afterCompletion

中斷流程

1、攔截器

HandlerInterceptor3和HandlerInterceptor4 與 之前的 HandlerInteceptor1和HandlerInterceptor2一樣,只是在HandlerInterceptor4的preHandle方法返回false:

?
1
2
3
4
5
6
7
     @Override
     public  boolean  preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler)  throws  Exception {
         System.out.println( "===========HandlerInterceptor1 preHandle" );
         response.getWriter().print( "break" ); //流程中斷的話需要我們進行響應的處理
         return  false ; //返回false表示流程中斷    
}

2、控制器

流程中斷不會執行到控制器,使用之前的TestController控制器。

3、Spring配置文件chapter5-servlet.xml

2、控制器

流程中斷不會執行到控制器,使用之前的TestController控制器。

3、Spring配置文件chapter5-servlet.xml

1
2
3
4
         System.out.println( "===========HandlerInterceptor1 preHandle" );
         response.getWriter().print( "break" ); //流程中斷的話需要我們進行響應的處理
         return  false ; //返回false表示流程中斷    
}

2、控制器

流程中斷不會執行到控制器,使用之前的TestController控制器。

3、Spring配置文件chapter5-servlet.xml

?
1
2
3
4
< bean  id = "handlerInterceptor3" 
         return  false
相關文章
相關標籤/搜索