springboot系列文章之過濾器 vs 攔截器

原文博客地址: pjmike的博客java

前言

以前實際開發項目的時候,雖然有用過濾器和攔截器,可是理解上仍是有點懵懵懂懂的,沒有完全明白,這篇文章就來仔細剖析下這兩者的區別與聯繫。git

過濾器

過濾器Filter,是在Servlet規範中定義的,是Servlet容器支持的,該接口定義在 javax.servlet包下,主要是在客戶端請求(HttpServletRequest)進行預處理,以及對服務器響應(HttpServletResponse)進行後處理。接口代碼以下:github

package javax.servlet;

import java.io.IOException;

public interface Filter {
    void init(FilterConfig var1) throws ServletException;

    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

    void destroy();
}
複製代碼

對上面三個接口方法進行分析:web

  • init(FilterConfig): 初始化接口,在用戶自定義的Filter初始化時被調用,它與Servlet的 init方法的做用是同樣的。
  • doFilter(ServletRequest,ServletResponse,FilterChain): 在每一個用戶的請求進來時這個方法都會被調用,並在Servlet的service方法以前調用(若是咱們是開發Servlet項目),而FilterChain就表明當前的整個請求鏈,經過調用 FilterChain.doFilter能夠將請求繼續傳遞下去,若是想攔截這個請求,能夠不調用FilterChain.doFilter,那麼這個請求就直接返回了,因此Filter是一種責任鏈設計模式,在spring security就大量使用了過濾器,有一條過濾器鏈。
  • destroy: 當Filter對象被銷燬時,這個方法被調用,注意,當Web容器調用這個方法以後,容器會再調用一次doFilter方法。

自定義Filter過濾器

在springboot自定義Filter類以下:spring

@Component
public class MyFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(MyFilter.class);
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("filter init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("doFilter");
        //對request,response進行預處理
        //TODO 進行業務邏輯
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        logger.info("filter destroy");
    }
}

複製代碼

FilterRegistrationBean方式

在springboot中提供了FilterRegistrationBean方式,此類提供setOrder方法,能夠爲多個filter設置排序值。代碼以下:設計模式

@Configuration
public class FilterConfig {
    /** * 配置一個Filter註冊器 * * @return */
    @Bean
    public FilterRegistrationBean filterRegistrationBean1() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter1());
        registrationBean.setName("filter1");
        //設置順序
        registrationBean.setOrder(10);
        return registrationBean;
    }
    @Bean
    public FilterRegistrationBean filterRegistrationBean2() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter2());
        registrationBean.setName("filter2");
        //設置順序
        registrationBean.setOrder(3);
        return registrationBean;
    }
    @Bean
    public Filter filter1() {
        return new MyFilter();
    }

    @Bean
    public Filter filter2() {
        return new MyFilter2();
    }
}

複製代碼

攔截器

攔截器是Spring提出的概念,它的做用於過濾器相似,能夠攔截用戶請求並進行相應的處理,它能夠進行更加精細的控制。springboot

在SpringMVC中,DispatcherServlet捕獲每一個請求,在到達對應的Controller以前,請求能夠被攔截器處理,在攔截器中進行前置處理後,請求最終纔到達Controller。bash

攔截器的接口是 org.springframework.web.servlet.HandlerInterceptor接口,接口代碼以下:服務器

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

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

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}
複製代碼

接口方法解讀:ide

  • preHandle方法:對客戶端發過來的請求進行前置處理,若是方法返回true,繼續執行後續操做,若是返回false,執行中斷請求處理,請求不會發送到Controller
  • postHandler方法:在請求進行處理後執行,也就是在Controller方法調用以後處理,固然前提是以前的 preHandle方法返回 true。具體來講,postHandler方法會在DispatcherServlet進行視圖返回渲染前被調用,也就是說咱們能夠在這個方法中對 Controller 處理以後的ModelAndView對象進行操做
  • afterCompletion方法: 該方法在整個請求結束以後執行,固然前提依然是 preHandle方法的返回值爲 true才行。該方法通常用於資源清理工做

自定義攔截器

public class MyInterceptor implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("preHandle....");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info("postHandle...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.info("afterCompletion...");
    }
}
複製代碼

註冊攔截器同時配置攔截器規則

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(handlerInterceptor())
                //配置攔截規則
                .addPathPatterns("/**");
    }
    @Bean
    public HandlerInterceptor handlerInterceptor() {
        return new MyInterceptor();
    }
}
複製代碼

多個攔截器協同工做

在springMVC中咱們能夠實現多個攔截器,並依次將他們註冊進去,以下:

public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(handlerInterceptor())
                .addPathPatterns("/**");
        registry.addInterceptor(handlerInterceptor2())
                .addPathPatterns("/**");
    }
複製代碼

攔截器的順序也跟他們註冊時的順序有關,至少 preHandle方法是這樣,下圖表示了兩個攔截器協同工做時的執行順序:

img

上圖出自慕課網

後臺打印日誌也輸出了相同的執行順序:

io-9999-exec-2] c.p.filter.interceptor.MyInterceptor     : preHandle....
2018-09-13 12:13:31.292  INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor2    : preHandle2....
2018-09-13 12:13:31.388  INFO 9736 --- [nio-9999-exec-2] c.p.filter.controller.HelloController    : username:pjmike,password:123456
2018-09-13 12:13:31.418  INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor2    : postHandle2...
2018-09-13 12:13:31.418  INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor     : postHandle...
2018-09-13 12:13:31.418  INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor2    : afterCompletion2...
2018-09-13 12:13:31.418  INFO 9736 --- [nio-9999-exec-2] c.p.filter.interceptor.MyInterceptor     : afterCompletion...
複製代碼

攔截器與過濾器之間的區別

從上面對攔截器與過濾器的描述來看,它倆是很是類似的,都能對客戶端發來的請求進行處理,它們的區別以下:

  • 做用域不一樣
    • 過濾器依賴於servlet容器,只能在 servlet容器,web環境下使用
    • 攔截器依賴於spring容器,能夠在spring容器中調用,無論此時Spring處於什麼環境
  • 細粒度的不一樣
    • 過濾器的控制比較粗,只能在請求進來時進行處理,對請求和響應進行包裝
    • 攔截器提供更精細的控制,能夠在controller對請求處理以前或以後被調用,也能夠在渲染視圖呈現給用戶以後調用
  • 中斷鏈執行的難易程度不一樣
    • 攔截器能夠 preHandle方法內返回 false 進行中斷
    • 過濾器就比較複雜,須要處理請求和響應對象來引起中斷,須要額外的動做,好比將用戶重定向到錯誤頁面

小結

簡單總結一下,攔截器相比過濾器有更細粒度的控制,依賴於Spring容器,能夠在請求以前或以後啓動,過濾器主要依賴於servlet,過濾器能作的,攔截器基本上都能作。

參考資料 & 鳴謝

相關文章
相關標籤/搜索