Spring攔截器和過濾器

什麼是攔截器

        攔截器(Interceptor): 用於在某個方法被訪問以前進行攔截,而後在方法執行以前或以後加入某些操做,其實就是AOP的一種實現策略。它經過動態攔截Action調用的對象,容許開發者定義在一個action執行的先後執行的代碼,也能夠在一個action執行前阻止其執行。同時也是提供了一種能夠提取action中可重用的部分的方式。java

攔截器做用

        攔截用戶的請求並進行相應的處理,好比:判斷用戶是否登錄,是否在可購買時間內,記錄日誌信息等。web

Spring中兩種實現方式

實現HandlerInterceptor接口

         經過實現HandlerInterceptor接口, 通常經過繼承HandlerInterceptorAdapter抽象類實現。spring

handlerInterceptor接口實現  展開原碼
DispatcherServlet處理流程:DispatcherServlet處理請求時會構造一個Excecution Chain,即(可能多個)攔截器和真正處理請求的Handler
即Interceptor是鏈式調用的。

preHandle: 在執行Handler以前進行,即Controller方法調用以前執行,主要進行初始化操做。app

postHandle: 在執行Handler以後進行,即Controller 方法調用以後執行,主要對ModelAndView對象進行操做。框架

afterCompletion: 在整個請求結束以後,即渲染對應的視圖以後執行, 主要進行資源清理工做。async

draw.io evaluation version
PreHandle
PostHandle
AfterCompletion
PreHandle
PostHandle
AfterCompletion
Interceptor1
Interceptor2
reqeust
handler
具體controller方法
response
 

 

注意事項: 每一個Interceptor的調用會依據它在xml文件中聲明順序依次執行。ide

DispatcherServlet中攔截器相關

DispatcherServlet中doDispatch  摺疊原碼
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
protected  void  doDispatch(HttpServletRequest request, HttpServletResponse response)  throws  Exception {
         HttpServletRequest processedRequest = request;
         HandlerExecutionChain mappedHandler =  null ;
         boolean  multipartRequestParsed =  false ;
 
         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
         try  {
             ModelAndView mv =  null ;
             Exception dispatchException =  null ;
 
             try  {
                 //檢查是不是請求multipart,如文件上傳
                 processedRequest = checkMultipart(request);
                 multipartRequestParsed = processedRequest != request;
 
                 // Determine handler for the current request.
                 //請求處處理器(頁面控制器)的映射,經過HanMapping進行映射
                 mappedHandler = getHandler(processedRequest,  false );
                 if  (mappedHandler ==  null  || mappedHandler.getHandler() ==  null ) {
                     noHandlerFound(processedRequest, response);
                     return ;
                 }
 
                 // Determine handler adapter for the current request.
                 //處理適配,將處理器包裝成相應的適配器
                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
                 // Process last-modified header, if supported by the handler.
                 String method = request.getMethod();
                 boolean  isGet =  "GET" .equals(method);
                 if  (isGet ||  "HEAD" .equals(method)) {
                     long  lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                     if  (logger.isDebugEnabled()) {
                         String requestUri = urlPathHelper.getRequestUri(request);
                         logger.debug( "Last-Modified value for ["  + requestUri +  "] is: "  + lastModified);
                     }
                     if  ( new  ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                         return ;
                     }
                 }
                 //這裏是關鍵,執行處理器相關的攔截器的預處理
                 if  (!mappedHandler.applyPreHandle(processedRequest, response)) {
                     return ;
                 }
 
                 try  {
                     // Actually invoke the handler.
                     //由適配器執行處理器(調用處理器相應功能處理方法)
                     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                 }
                 finally  {
                     if  (asyncManager.isConcurrentHandlingStarted()) {
                         return ;
                     }
                 }
 
                 applyDefaultViewName(request, mv);
                 //執行處理器相關的攔截器的後處理
                 mappedHandler.applyPostHandle(processedRequest, response, mv);
             }
             catch  (Exception ex) {
                 dispatchException = ex;
             }
             //該方法中會調用triggerAfterCompletion,執行處理器相關的攔截器的完成後處理
             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
         }
         catch  (Exception ex) {
             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
         }
         catch  (Error err) {
             triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
         }
         finally  {
             if  (asyncManager.isConcurrentHandlingStarted()) {
                 // Instead of postHandle and afterCompletion
                 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                 return ;
             }
             // Clean up any resources used by a multipart request.
             if  (multipartRequestParsed) {
                 cleanupMultipart(processedRequest);
             }
         }
     }
HandlerExecutionChain中applyPreHandle  摺疊原碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
boolean  applyPreHandle(HttpServletRequest request, HttpServletResponse response)  throws  Exception {
         if  (getInterceptors() !=  null ) {
             //順序執行攔截器的preHandle方法,若是返回false,則調用triggerAfterCompletion方法
             for  ( int  i =  0 ; i < getInterceptors().length; i++) {
                 HandlerInterceptor interceptor = getInterceptors()[i];
                 if  (!interceptor.preHandle(request, response,  this .handler)) {
                     triggerAfterCompletion(request, response,  null );
                     return  false ;
                 }
                 this .interceptorIndex = i;
             }
         }
         return  true ;
     }
applyPostHandle  摺疊原碼
1
2
3
4
5
6
7
8
9
10
void  applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)  throws  Exception {
     if  (getInterceptors() ==  null ) {
         return ;
     }
     //逆序執行攔截器的postHandle方法
     for  ( int  i = getInterceptors().length -  1 ; i >=  0 ; i--) {
         HandlerInterceptor interceptor = getInterceptors()[i];
         interceptor.postHandle(request, response,  this .handler, mv);
     }
}
triggerAfterCompletion  摺疊原碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void  triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
         throws  Exception {
 
     if  (getInterceptors() ==  null ) {
         return ;
     }
     //逆序執行攔截器的afterCompletion方法
     for  ( int  i =  this .interceptorIndex; i >=  0 ; i--) {
         HandlerInterceptor interceptor = getInterceptors()[i];
         try  {
             interceptor.afterCompletion(request, response,  this .handler, ex);
         }
         catch  (Throwable ex2) {
             logger.error( "HandlerInterceptor.afterCompletion threw exception" , ex2);
         }
     }
}

實現WebRequestInterceptor接口    

WebRequestInterceptor接口定義  摺疊原碼
1
2
3
4
5
6
7
8
9
10
11
//與HandlerInterceptor的區別在於沒法終止訪問請求
public  interface  WebRequestInterceptor {
 
     //返回類行爲void,與HandlerInterceptor區別就體如今這裏
     void  preHandle(WebRequest request)  throws  Exception;
 
     void  postHandle(WebRequest request, ModelMap model)  throws  Exception;
 
     void  afterCompletion(WebRequest request, Exception ex)  throws  Exception;
 
}

       

------------------------------------------------post

SpringMVC的攔截器Interceptor和過濾器Filter功能很是類似,使用場景也差很少,看起來難以區分。好比二者都能在代碼先後插入執行片斷,均可以用來實現一些公共組件的功能複用(權限檢查、日誌記錄等),其實它們並不同,首先了解一下Interceptor和Filter。this

一.Interceptor

Interceptor是Spring攔截器,要實現一個攔截器功能能夠繼承Spring的HandlerInterceptor接口:lua

package com.hpx.xiyou.wuKong.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Component
public class sanZangInterceptor implements HandlerInterceptor{
    static public final Logger logger = LoggerFactory.getLogger(sanZangInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //System.out.println("interceptortest pre");
        logger.info("interceptortest pre");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        //System.out.println("interceptortest post");
        logger.info("interceptortest post");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        //System.out.println("interceptortest after");
        logger.info("interceptortest after");
    }
}

HandlerInterceptor接口有三個須要實現的方法:preHandle(),postHandle()和afterCompletion()。

preHandle方法將在請求處理以前調用,SpringMVC中的Interceptor是鏈式調用的,每一個Interceptor的調用都根據它的聲明順序依次執行,且最早執行其preHandle方法,因此能夠在該方法中進行一些前置初始化操做或是預處理。該方法的返回值是布爾類型,若是返回false,表示請求結束,後續的Interceptor和Controller都不會再執行了,若是返回true就執行下一個攔截器的preHandle方法,一直到最後一個攔截器preHandle方法執行完成後調用當前請求的Controller方法。

postHandle方法是在當前請求進行處理以後,也就是Controller方法調用結束以後執行,可是它會在DispatcherServlet進行視圖渲染以前被調用,因此能夠在這個方法中能夠對Controller處理以後的ModelAndView對象進行操做。postHandle方法被調用的方向跟preHandle是相反的,也就是說先聲明的Interceptor的postHandle方法反然後執行。

afterCompletion方法須要當前對應的Interceptor的preHandle方法的返回值爲true時纔會執行。該方法會在整個請求結束以後,也就是在DispatcherServlet渲染了對應的視圖以後執行,這個方法的主要做用是用於資源清理工做。

實現一個interceptor攔截器類後,須要在配置中配置使它生效:實現 WebMvcConfigurerAdapter並重寫 addInterceptors,同時在這個方法裏設置要過濾的URL。

package com.hpx.xiyou.wuKong.Adapter;

import com.hpx.xiyou.wuKong.aop.sanZangInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;


@Configuration
public class WebConfigurerAdapter extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new sanZangInterceptor()).addPathPatterns("/wukong/**");

    }
}

以上配置生效後,當訪問/wukong/**類型url時,控制檯輸出以下,其中controller爲controller方法中的打印信息:

interceptortest pre
controller
interceptortest post
interceptortest after

二.Filter

Filter是Spring過濾器,要定義一個Filter類有如下步驟:

首先定義一個Filter類,繼承javax.servlet.Filter類,重寫其init、doFilter、destroy方法。init()方法會在Filter初始化後進行調用,在init()方法裏面咱們能夠經過FilterConfig訪問到初始化參數( getInitParameter()或getInitParameters() )、ServletContext (getServletContext)和當前Filter部署的名稱( getFilterName() )等信息。destroy()方法將在Filter被銷燬以前調用。而doFilter()方法則是真正進行過濾處理的方法,在doFilter()方法內部,咱們能夠過濾請求的request和返回的response,同時咱們還能夠利用FilterChain把當前的request和response傳遞給下一個過濾器或Servlet進行處理。

public  class  FilterTest  implements  Filter {
     @Autowired
     private  PointService pointService;
 
     @Override
     public  void  init(FilterConfig filterConfig)  throws  ServletException {
         System.out.println( "init yes" );
     }
 
     @Override
     public  void  doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)  throws  IOException, ServletException {
         System.out.println( "filter test" );
         filterChain.doFilter(servletRequest, servletResponse);   // 傳遞給下一個Filter進行處理
         return ;
     }
 
     @Override
     public  void  destroy() {
         System.out.println( "destroy yes" );
     }
}

而後在配置中使該Filter生效:

<filter>
     <filter-name>filtertest</filter-name>
     <filter- class >FilterTest</filter- class >
</filter>
<filter-mapping>
     <filter-name>filtertest</filter-name>
     <url-pattern>/point/*</url-pattern>
</filter-mapping>

這樣,當咱們訪問/point/*類型的url,控制檯輸出以下:

init yes

filter test

controller

三.比較

同時配置過濾器和攔截器而後請求,結果以下:

init yes

 filter test

 interceptortest pre

controller

interceptortest post

interceptortest after

能夠看到filter優先於interceptor被調用。

過濾器和攔截器主要區別以下:

1.兩者適用範圍不一樣。Filter是Servlet規範規定的,只能用於Web程序中,而攔截器既能夠用於Web程序,也能夠用於Application、Swing程序中。

2.規範不一樣。Filter是在Servlet規範定義的,是Servlet容器支持的,而攔截器是在Spring容器內的,是Spring框架支持的。

3.使用的資源不一樣。同其餘代碼塊同樣,攔截器也是一個Spring的組件,歸Spring管理,配置在Spring文件中,所以能使用Spring裏的任何資源、對象(各類bean),而Filter不行。

4.深度不一樣。Filter只在Servlet先後起做用,而攔截器可以深刻到方法先後、異常跑出先後等,攔截器的使用有更大的彈性。

相關文章
相關標籤/搜索