Java 中的過濾器和攔截器有什麼區別麼?

今天我們不談框架,我們來說說這個 Java 基礎中的過濾器和攔截器,爲什麼要談呢?其實就有粉絲會問了不起,這個過濾器和攔截器他們有什麼區別呢?爲什麼面試官在面試的時候經常會問到這個過濾器和攔截器的區別,以及他們分別對應的應用場景呢?

今天了不起就來說說這個過濾器和攔截器。

過濾器

過濾器 Filter

什麼是過濾器

過濾器是 Servlet 的高級特性之一,是實現 Filter 接口的 Java 類。其基本功能就是對 servlet 的調用進行干預,在 Servlet 請求和響應的過程中增加一些特定的功能。可以使用過濾器實現的功能有: 統一編碼,URL 級別的權限訪問控制,過濾敏感詞彙,壓縮請求信息.

我們來通過代碼來看 Filter 的執行過程

public interface Filter {
    //用於執行過濾器的初始化工作,web容器會在web項目啓動時自動調用該方法
    default void init(FilterConfig filterConfig) throws ServletException {
    }
 
    //當請求和響應被過濾器攔截後,都會交給doFilter來處理:其中兩個參數分別是被攔截request和response對象,可以使用chain的doFliter方法來放行
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
    //用於釋放關閉Filter對象打開的資源,在web項目關閉時,由web容器自動調用該方法
    default void destroy() {
    }
}

在 Filter 中我們可以看到有三個方法,分別是

至於每個方法的作用,了不起已經把他們的功能寫在了註釋上面,他們實際上就是三個步驟:

  1. 初始化

  2. 處理請求和相應過濾,完成操作

  3. 最後釋放資源

而實現一個自定義 Filter 也是比較簡單的,我們來實現一個簡單的自定義的 Filter

@Component
@Slf4j
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("初始化過濾器:" + filterConfig.getFilterName());
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 對請求進行過濾操作處理
        log.info("MyFilter 開始對請求進去過濾操作!");
        String requestURI = httpRequest.getRequestURI();
        log.info("請求地址:" + requestURI);

        // 繼續執行下一個 Filter,如果沒有其他 Filter 的話就執行 Controller 層代碼
        long startTime = System.currentTimeMillis();
        chain.doFilter(request, response);

        // 執行完用戶請求後,回到這裏對 response 響應內容做一些處理
        long endTime = System.currentTimeMillis();
        log.info("請求處理完畢,所花費時間爲:" + (endTime - startTime) + "ms");
    }

    @Override
    public void destroy() {
        log.info("銷燬過濾器 MyFilter");
    }

}

至於 Filter 的配置的話,了不起都不用解釋,現在幾乎沒有再去配置 xml 的方式了,都是配置類或者註解的方式。

註解方式:

@WebFilter(filterName = "myFilter",urlPatterns = {"/*"})

配置類方式:

@Configuration
public class MyFilterConfig {
 
    @Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean fb = new FilterRegistrationBean();
        //設置filter啓動順序
        fb.setOrder(1);
        fb.setFilter(new MyFilter());
        fb.addInitParameter("phone","183****5510");
        //設置攔截請求規則,這裏攔截所有請求
        fb.addUrlPatterns("/*");
        return fb;
    }
}

我們看完了這個過濾器,接下來就得看看這個攔截器了。

攔截器

攔截器 Interceptor

什麼是攔截器

Spring MVC 中的攔截器Interceptor類似於 ServLet 中的過濾器Filter,它主要用於攔截用戶請求並作出相應的處理。例如通過攔截器可以進行權限驗證、記錄請求信息的日誌、判斷用戶是否登錄等。

老規矩,直接看源代碼

 
public interface HandlerInterceptor {
    //預處理方法,本方法在控制器方法(MyController的方法)之前執        行,用戶的請求最先到達此方法,在這個方法中可以獲取請求的信息,驗證請求是否符合要求。以驗證用戶是否登錄,驗證用戶是否有權限訪問某個鏈接地址(url)。如果返回true則放行,返回false則攔截
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
    //後處理方法。在controller中的方法之後執行的。能夠獲取到處理器方法的返回值 mv,可以修改mv中的數據和視圖。可以影響到最後的執行結果。主要是對原來的執行結果做二次修正
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }
    //最後執行的方法,在頁面渲染之後執行。在請求處理完成後執行的,框架中規定是當你的視圖處理完成後,對視圖進行了forword。就任務請求處理完成。
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

其實也是三個步驟:

  1. 預處理

  2. 後處理

  3. 最後執行

至於每一步的含義和內容,了不起同樣的再註釋中表明瞭。

同樣的,自定義實現一個攔截器也很簡單:

public class MyInterceptor implements HandlerInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyInterceptor.class);

    private static final ThreadLocal<Long> START_THREAD_LOCAL = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String uri = request.getRequestURI();
        LOGGER.info(uri + " preHandle");
        Long startTime = System.currentTimeMillis();    //獲取開始時間
        START_THREAD_LOCAL.set(startTime);  //線程綁定變量(該數據只有當前請求的線程可見)
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        String uri = request.getRequestURI();
        LOGGER.info(uri + " postHandle");
        Long startTime = START_THREAD_LOCAL.get();//得到線程綁定的局部變量(開始時間)
        Long endTime = System.currentTimeMillis();  //2、結束時間
        Long time = endTime - startTime;
        LOGGER.info("http request all time: " + time + "ms");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                Exception ex) throws Exception {
        String uri = request.getRequestURI();
        LOGGER.info(uri + " afterCompletion");
        if (START_THREAD_LOCAL != null) {
            START_THREAD_LOCAL.remove();    // 移除ThreadLocal中的局部變量
        }
    }
}

我們自定義一個獲取並返回某個靜態資源的內容以及整個請求所花費的時間攔截器, 一般這個還是比較有用的,而且還可以加一個請求訪問的,然後來處理方法執行時間的。

配置攔截器其實在項目中是非常的常見

@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
 
    /**
     * 配置攔截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(myInterceptor())
                .addPathPatterns("/api/v1/**")//需要攔截的請求
                .addPathPatterns("/api/v1/**")//需要攔截的請求
                .excludePathPatterns("/api/debug/**")//不攔截的請求
                .excludePathPatterns("api/page/getName");//不攔截的請求
    }
 
    /**
     * 注入攔截器到spring容器
     * @return
     */
    @Bean
    public MyInterceptor myInterceptor(){
        return new MyInterceptor();
    }
}

我們看完了過濾器和攔截器的實現,接下來就得來看看這個過濾器和攔截器之間的區別了。

  1. 依賴點不同
  1. 實現原理不同
  1. 作用域不同
  1. 生命週期不同

這是區別,其實二者的相同點也是有的,比如,攔截器和過濾器實際上都是 Spring 中 AOP 的體現,都能實現一些權限和日誌方面的功能。

你知道他們是什麼區別了麼?

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