聊聊過濾器與攔截器區別

1. 過濾器(Filter)

Servlet 中的過濾器 Filter 實現了 javax.servlet.Filter 接口的服務器端程序,主要用途是設置字符集(CharacterEncodingFilter)、控制權限、控制轉向、用戶是否已經登陸、有沒有權限訪問該頁面等。

其工作原理是,只要你在 web.xml 文件配置好要攔截的客戶端請求,它都會幫你攔截到請求。此時,其實你可以對請求或響應(request、response)統一設置編碼;它隨 web 應用啓動而啓動,只初始化一次,以後就可以攔截相關請求。只有當你的 web 應用停止或重新部署的時候才銷燬。

1.1 Filter 的用處

Filter 有如下幾個種類。

1.2 Filter 的種類

Filter 可以負責攔截多個請求或響應,一個請求或響應也可以被多個 Filter 攔截。

1.3 創建 Filter 的步驟

  1. 創建 Filter 處理類,並實現 javax.servlet.Filter 接口;

  2. web.xml 文件中配置 Filter(或者使用 @WebFilter 註解)。

javax.servlet.Filter 接口中定義了三個方法:

doFilter 方法就是對每個請求及響應增加的額外處理。該方法可以實現對用戶請求進行預處理(ServletRequest request),也可實現對服務器響應進行後處理 (ServletResponse response)。它們的分界線爲,是否調用了 chain.doFilter()。執行該方法之前,即對用戶請求進行預處理;執行該方法之後,即對服務器響應進行後處理。

2. 攔截器(Interceptor)

攔截器是在面向切面編程中應用的,就是 service 或一個方法前 / 後調用一個方法。是基礎 Java 的反射機制。攔截不是在 web.xml,而是在 AOP(Aspect-Oriented Programming)中用於某個方法或字段被訪問之前,進行攔截。然後在之前或之後加入某些操作,甚至在拋出異常的時候做業務邏輯的操作。攔擊器是 AOP 的一種實現策略。

2.1 攔截器的實現方式

SpringMVC 中的 Interceptor 攔截請求是通過 HandlerInterceptor 來實現的。在 SpringMVC 中定義 Interceptor 主要有兩種方式:

Interceptor 中的方法:

preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法

顧名思義,該方法將在請求處理之前進行調用。

SpringMVC 中的 Interceptor 是鏈式調用。在一個應用中或者說是在一個請求中可以同時存在多個 Interceptor,每個 Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是 Interceptor 中的 preHandle 方法。

所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值 Boolean 類型的,當它返回爲 false 時,表示請求結束,後續的 Interceptor 和 Controller 都不會再執行;當返回值爲 true 時就會繼續調用下一個 Interceptor 的 preHandle 方法。如果已經是最後一個 Interceptor 的時候就會是調用當前請求的 Controller 方法。

postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法

由 preHandle 方法的解釋我們知道,這個方法包括後面要說到的 afterCompletion 方法都只能是在當前所屬的 Interceptor 的 preHandle 方法的返回值爲 true 時才能被調用。

postHandle 方法,顧名思義就是在當前請求進行處理之後,也就是 Controller 方法調用之後執行,但是它會在 DispatcherServlet 進行視圖返回渲染之前被調用。所以,我們可以在這個方法中對 Controller 處理之後的 ModelAndView 對象進行操作。

postHandle 方法被調用的方向跟 preHandle 是相反的。也就是說,先聲明的 Interceptor 的 postHandle 方法反而會後執行。這和 Struts2 裏面的 Interceptor 的執行過程有點類似。Struts2 裏面的 Interceptor 的執行過程也是鏈式的,只是在 Struts2 裏面需要手動調用 ActionInvocation 的 invoke 方法來觸發對下一個 Interceptor 或者是 Action 的調用,然後每一個 Interceptor 中在 invoke 方法調用之前的內容都是按照聲明順序執行的,而 invoke 方法之後的內容就是反向的。

afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法

該方法也是需要當前對應的 Interceptor 的 p reHandle 方法的返回值爲 true 時纔會執行。顧名思義,該方法將在整個請求結束之後,也就是在 DispatcherServlet 渲染了對應的視圖之後執行。這個方法的主要作用是用於進行資源清理工作的。

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter{
    private static final Logger logger = Logger.getLogger(ExecuteTimeInterceptor.class);
    //before the actual handler will be executed
    public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler)
        throws Exception {
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        return true;
    }
    //after the handler is executed
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
        throws Exception {
        long startTime = (Long)request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        //統計耗時
        long executeTime = endTime - startTime;
        //modified the exisitng modelAndView
        modelAndView.addObject("executeTime",executeTime);
        //log it
        if(logger.isDebugEnabled()){
           logger.debug("[" + handler + "] executeTime : " + executeTime + "ms");
        }
    }
}

非 SpringBoot 項目

使用 mvc:interceptors 標籤來聲明需要加入到 SpringMVC 攔截器鏈中的攔截器。

<mvc:interceptors>  
<!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請求 -->  
<bean/>  
    <mvc:interceptor>  
         <mvc:mapping path="/**"/>  
         <mvc:exclude-mapping path="/parent/**"/>  
         <bean />  
    </mvc:interceptor>  
    <mvc:interceptor>  
         <mvc:mapping path="/parent/**"/>  
         <bean />  
    </mvc:interceptor>  
</mvc:interceptors>

可以利用 mvc:interceptors 標籤聲明一系列的攔截器,然後它們就可以形成一個攔截器鏈。攔截器的執行順序是按聲明的先後順序執行的,先聲明的攔截器中的 preHandle 方法會先執行,然而它的 postHandle 方法和 afterCompletion 方法卻會後執行。

在 mvc:interceptors 標籤下聲明 interceptor 主要有兩種方式:

經過上述兩步之後,定義的攔截器就會發生作用對特定的請求進行攔截了。

Spring Boot 項目

配置攔截器

@Configuration
//@Configurationpublic
public class WebAppConfigurer implements WebMvcConfigurer {
    /**
     * 配置攔截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 多個攔截器組成一個攔截器鏈
        registry.addInterceptor(new ExecuteTimeInterceptor()).addPathPatterns("/**");
        //API限流攔截
        registry.addInterceptor(accessLimitAjaxInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html");
        registry.addInterceptor(accessInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html");
    }
}

2.2 攔截器(interceptor)使用

  1. 請求到達 DispatcherServlet;

  2. DispatcherServlet 發送至 Interceptor,執行 preHandler;

  3. 請求到達 Controller;

  4. 請求結束後,執行 postHandler。

3. 過濾器(Filter)與 攔截器(Interceptor)的區別

Spring 的 Interceptor(攔截器)與 Servlet 的 Filter 有相似之處,比如二者都是 AOP 編程思想的體現,都能實現權限檢查、日誌記錄等。

不同之處總結如下:

3.1 執行順序

用戶請求 -> 過濾前 -> 攔截前 -> Action 處理 -> 攔截後 -> 過濾後 -> 響應

4. 過濾器(Filter)與 攔截器(Interceptor)常見用途

Request Filters 可以實現:

Response Filters 可以實現

5. 總結

過濾器

所謂過濾器顧名思義是用來過濾的。在 Java Web 中,傳入的 request、response 提前過濾掉一些信息,或者提前設置一些參數;然後再傳入 Servlet 或者 Struts 的 action 進行業務邏輯。比如,過濾掉非法 URL(不是 login.do 的地址請求,如果用戶沒有登陸都過濾掉),或者在傳入 Servlet 或者 Struts 的 action 前統一設置字符集,或者去除掉一些非法字符(聊天室經常用到的,一些罵人的話)。

Filter 流程是線性的, URL 傳來之後,檢查之後,可保持原來的流程繼續向下執行,被下一個 filter、servlet 接收等。

Java 的攔截器

主要是用在插件上,擴展件上比如 Hibernate Spring Struts2 等 有點類似面向切片的技術,在用之前先要在配置文件即 XML 文件裏進行對應的聲明。

攔截器(Interceptor)是基於 Java 的反射機制,而過濾器(Filter)是基於函數回調。從靈活性上說攔截器功能更強大些,Filter 能做的事情,攔截器都能做,而且可以在請求前、請求後執行,比較靈活。Filter 主要是針對 URL 地址做一個編碼的事情、過濾掉沒用的參數、安全校驗(比如登錄不登錄之類),太細的話,還是建議用攔截器。

轉自:小余哥,

鏈接:xiaoyuge.work/filter-interceptor/index.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/qWQHkFzdBorCYJrMhrz9Mg