過濾器(Filter)和攔截器(Interceptor)

一、引言

在面向對象編程(OOP)的過程中,很容易通過繼承、多態來解決縱向擴展。但對于橫向的功能,如登記所有的客戶端請求耗時、統一開啟事務等功能,OOP 無能為力。面向切面編程(AOP)的編程思想是對 OOP 的一個補充,本篇所討論的過濾器和攔截器都屬于 AOP 的具體實現。

二、過濾器 Filter

過濾器(Filter)的預處理發生在請求進入容器后,未進入 Servlet 之前。相應的,其后處理發生在 Servlet 處理完成后,返回給前端之前。所以過濾器的 doFilter(ServletRequest request, ServletResponse response, FilterChain chain)的入參是ServletRequest,而不是httpservletrequest。因為過濾器是在httpservlet之前。

@Override
public void init(FilterConfig arg0) throws ServletException {   
}

@Override 
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        log.info("before...");
        chain.doFilter(request, response);
        log.info("after...");
}

@Override
public void destroy() { 
}

FilterServlet都是由容器負責創建和銷毀的。在一個應用程序中,一個Filter只會被創建和銷毀一次。web 應用程序啟動時,容器調用public void init(FilterConfig filterConfig) throws ServletException方法初始化Filter;web 應用程序被移除或者是容器關閉時,調用public void destroy()銷毀Filter

Filter中聲明的doFilter(ServletRequest request, ServletResponse response, FilterChain chain)方法,用于實現具體的過濾邏輯。其中FilterChain chain參數是一個過濾鏈對象,它包含了用戶定義的一系列過濾器,這些過濾器根據其定義順序依次被執行。通過執行chain.doFilter(request, response)方法可以跳過當前過濾器處理邏輯,執行過濾鏈中的下一個過濾器。事實上調用ServletdoService()方法是在chain.doFilter(request, response)這個方法中進行的。

三、攔截器 Interceptor

通過繼承HandlerInterceptorAdapter類并重寫下列三個方法可以快速的實現自定義攔截器:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {     
    return true;    
}

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {     
}

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {     
} 

上述方法分別實現了攔截器的預處理preHanlde、后處理postHandle(調用了Service并返回ModelAndView,但未進行頁面渲染)、返回處理afterCompletion(已經渲染了頁面)。

preHandle()方法實現了攔截器的預處理。如果存在多個攔截器,則會依次調用攔截器鏈中每一個攔截器的preHandle()方法:

  1. preHandle方法返回false時,DispatcherServlet處理器認為攔截器已經處理完了請求,不再繼續執行鏈中的其它攔截器和處理器,而是從當前攔截器往回執行所有攔截器的afterCompletion方法,退出攔截器鏈
  2. preHandle方法全為true時,執行下一個攔截器,直到所有攔截器執行完。再運行被攔截的 Controller。然后返回攔截器鏈,運行所有攔截器的postHandle方法,然后從最后一個攔截器往回執行所有攔截器的afterCompletion方法
  3. 當有攔截器拋出異常時,會從當前攔截器往回執行所有攔截器的afterCompletion方法

四、過濾器和攔截器的對比

二者在功能上很相似,其主要區別在于:

  1. 適用范圍:Filter 依賴于 Servlet 容器,屬于 Servlet 規范的一部分,只能用于 Web 程序;而攔截器是獨立存在的,由 Spring 框架支持,可以用于 Web 程序、Application、Swing 程序中
  2. 作用范圍:Filter 的執行由 Servlet 容器回調完成,過濾邏輯只能發生在 Servlet 調用前后;而攔截器基于 Java 反射機制,通常通過動態代理的方式來執行,能夠深入到方法的前后、異常拋出的前后等,使用起來更加靈活
  3. 可支配的資源:攔截器屬于Spring 組件,是通過 IoC 容器來管理,它能通過依賴注入的方式調用 Spring 里的任何資源、對象,例如 Service 對象、數據源、事務管理等;而 Filter 做不到,Filter 的生命周期由 Servlet 容器管理

五、運用場景

攔截器的主要應用場景有:

  1. 日志記錄:記錄請求信息的日志,以便進行信息監控、信息統計、計算PV(Page View)等。
  2. 權限檢查:如登錄檢測,進入處理器檢測用戶是否登錄,如沒有則跳轉到登錄頁面;
  3. 性能監控:有時候系統在某段時間莫名其妙的慢,可以通過攔截器在進入處理器之前記錄開始時間,在處理完后記錄結束時間,從而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄);
  4. 通用行為:讀取 cookie 得到用戶信息并將用戶對象放入請求,從而方便后續流程使用,還有如提取 Locale、Theme 信息等,只要是多個處理器都需要的即可使用攔截器實現。
  5. OpenSessionInView:如 hibernate,在進入處理器打開Session,在完成后關閉Session。

過濾器通常用于:

  • 在過濾器中修改字符編碼(CharacterEncodingFilter)、在過濾器中修改 HttpServletRequest 的一些參數(XSSFilter(自定義過濾器)),如:過濾低俗文字、危險字符等。

六、過濾器和攔截器的執行順序

綜上所述可知,Filter的執行順序在Interceptor之前。一圖勝千言:假設自定義了2個過濾器TestFilter1TestFilter2,2個攔截器TestInterceptorBaseInterceptor,其執行流程可能如下:

執行順序

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。