攔截器、過濾器、AOP的區別和聯絡

語言: CN / TW / HK

本文已參與「新人創作禮」活動,一起開啟掘金創作之路。

寫在前面

今天,和一位前輩聊了聊實習的事,關於java後端開發的幾個問題,真的是被虐的很慘啊(理解和能講明白完全就是兩回事啊)

其中,最沙雕的是,我竟然單純的以為過濾器(Filter)和 攔截器(Interceptor)是一個東西,然後前輩當場就diss我一頓

後來我回家翻了翻筆記發現我在學SpringMVC的時候是有學過攔截器的(還註明了==重點==兩個字),還將其應用到了登入驗證功能上

image-20220204195429856

後來我反思了下,SSM學完後,直接做了一個書店系統(很簡單的CRUD)就結束了,然後就直接==SpringBoot==了,

在裡面是直接用的==SpringSecurity==進行的許可權限制,漸漸地我就把==攔截器==給忘記了,

至於為啥把他當做過濾器,那是因為在學==JavaWeb==時確實把過濾器當做攔截器用過(比如登入功能)

至於為什麼能那麼寫,就請耐心的看我接下來的內容吧(嘿嘿)

image-20220204200015298

一、什麼是過濾器(Filter)

過濾器,顧名思義就是起到過濾篩選作用的一種事物,只不過相較於現實生活中的過濾器,這裡的過濾器過濾的物件是客戶端訪問的web資源,

也可以理解為一種預處理手段,對資源進行攔截後,將其中我們認為的雜質(使用者自己定義的)過濾,符合條件的放行,不符合的則攔截下來。

image-20220204202844297

1.1、過濾器常見的使用場景

  • 統一設定編碼
  • 過濾敏感字元
  • 登入校驗
  • URL級別的訪問許可權控制
  • 資料壓縮

1.2、Springboot 整合過濾器

  • Bean注入方式

==編寫Filter==

```java public class HeFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
      System.out.println("您已進入filter過濾器,您的請求正常,請繼續遵規則...");
      chain.doFilter(request, response);
  }

  @Override
  public void destroy() {
  }

} ```

==編寫Filter配置類==

java @Configuration public class ServletConfig { @Bean public FilterRegistrationBean heFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(new HeFilter()); registration.addUrlPatterns("/*"); return registration; } }

  • 註解方式

```java @Slf4j @Component // filterName就是當前類名稱,還有一個urlPattens的引數,這個引數表示要過濾的URL上的字尾名,是多引數,可以用陣列表示。 @WebFilter(value = "/hello") 或 (filterName = "f1", urlPatterns = {".html",".jsp","/hello"}) public class HelloFilter2 implements Filter {

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {

  }

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                       FilterChain filterChain) throws IOException, ServletException {
      log.info("進入到過濾器2啦");
      // 這裡可以進行一些條件判斷(假如要做登入驗證的話)
      filterChain.doFilter(servletRequest,servletResponse);
  }

  @Override
  public void destroy() {

  }

} ```

在主啟動類上加@ServletComponentScan("com.pandy.blog.filters") 指明filter所在位置的包。 若有多個filter,預設根據filter類名的字母倒敘排列,且@WebFilter註解方式的過濾器優先順序高於Bean注入方式配置的過濾器

1.3、過濾器(Filter)詳解

a) Filter是依賴於Servlet的,需要有Servlet的依賴。

b) init() 在容器初始化時執行,只執行一次。

c) doFilter() 目標請求之前攔截執行,攔截之後需要放行才開始執行目標方法。filterChain.doFilter(servletRequest,servletResponse);

d) destroy() 在容器銷燬時執行,只執行一次。

e) Filter可以攔截所有請求。包括靜態資源[css,js...]。

f) 基於函式回撥實現。

g) 過濾器只能在容器初始化時被呼叫一次。

二、什麼是攔截器(Interceptor)

==概念==

攔截器是springmvc提供的,類似於過濾器。主要用於攔截使用者請求並作相應的處理。

image-20220204205945784

2.1、攔截器的使用場景

a) 日誌記錄

b) 許可權校驗

c) 登入校驗

d) 效能檢測[檢測方法的執行時間]

==其實攔截器和過濾器很像,有些使用場景。無論選用誰都能實現。需要注意的使他們彼此的使用範圍,觸發機制。==

2.2、springboot整合攔截器

  • 編寫自定義攔截器類實現HandlerInterceptor介面或繼承其子類【推薦實現的方式,實現可以自動生成preHandle..】

```java @Component public class InterceptorDemo implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("攔截器preHandle在控制器方法執行前執行"); //true:表示放行 return true; }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
      System.out.println("攔截器postHandle在控制器方法執行後執行");
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      System.out.println("攔截器afterCompletion在請求完成後執行");
  }

} ```

  • 基於springmvc編寫配置類

```java @Configuration // 老版本呢是繼承WebMvcConfigurerAdapter不過新版本已經放棄了,推薦用下面的方式。 public class InterceptorConfig implements WebMvcConfigurer {

  @Autowired
  private InterceptorDemo interceptorDemo;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
      // ** 表示所有攔截路徑
      registry.addInterceptor(interceptorDemo).addPathPatterns("/**");
      // 或下面這種寫法  【若編寫自定義攔截器類沒有加@Component註解】
      registry.addInterceptor(new InterceptorDemo()).addPathPatterns("/**");
  }

}

```

2.3、攔截器詳情

a) 攔截器是依賴於SpringMVC的,需要有mvc的依賴。

b) preHandle() 在目標請求完成之前執行。有返回值Boolean型別,true:表示放行

c) postHandle() 在目標請求之完成後執行。

d) afterCompletion() 在整個請求完成之後【modelAndView已被渲染執行】。

e) 攔截器只能攔截==action==請求。不包括靜態資源==[css,js...]==。

f) 基於java==反射機制==實現。

g) 在攔截器的生命週期中,可以==多次==被呼叫。

```java 關於攔截器的preHandle()方法的引數說明: public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){

  }

  @param request        current HTTP request
  @param response       current HTTP response
  @param handler        chosen handler to execute, for type and/or instance evaluation   //選擇要執行的處理程式,用於型別和/或例項計算

```

三、攔截器與過濾器的區別

==一張圖告訴你答案==

image-20220204211710166

1、過濾器和攔截器觸發時機不一樣,過濾器是在請求進入容器後,但請求進入servlet之前進行預處理的。請求結束返回也是,是在servlet處理完後,返回給

前端之前。

2、攔截器可以獲取IOC容器中的各個bean,而過濾器就不行,因為攔截器是spring提供並管理的,spring的功能可以被攔截器使用,在攔截器裡注入一個

service,可以呼叫業務邏輯。而過濾器是JavaEE標準,只需依賴servlet api ,不需要依賴spring。

3、過濾器的實現基於回撥函式。而攔截器(代理模式)的實現基於反射

4、Filter是依賴於Servlet容器,屬於Servlet規範的一部分,而攔截器則是獨立存在的,可以在任何情況下使用。

5、Filter的執行由Servlet容器回撥完成,而攔截器通常通過動態代理(反射)的方式來執行。

6、Filter的生命週期由Servlet容器管理,而攔截器則可以通過IoC容器來管理,因此可以通過注入等方式來獲取其他Bean的例項,因此使用會更方便。

【重點】過濾器和攔截器非常相似,但是它們有很大的區別:

最簡單明瞭的區別就是==過濾器可以修改request,而攔截器不能==

過濾器需要在servlet容器中實現,攔截器可以適用於javaEE,javaSE等各種環境

攔截器可以==呼叫IOC容器中的各種依賴,而過濾器不能==

==過濾器只能在請求的前後使用,而攔截器可以詳細到每個方法==

==當有過濾器和攔截器時的執行流程:==

image-20220204212000434

四、AOP與過濾器、攔截器的區別

過濾器,攔截器攔截的是URL。AOP攔截的是類的元資料(包、類、方法名、引數等)。

過濾器並沒有定義業務用於執行邏輯前、後等,僅僅是請求到達就執行。

攔截器有三個方法,相對於過濾器更加細緻,有被攔截邏輯執行前、後等。 AOP針對具體的程式碼,能夠實現更加複雜的業務邏輯。

三者功能類似,但各有優勢,從過濾器--》攔截器--》切面,攔截規則越來越細緻。 執行順序依次是過濾器、攔截器、切面。

==三者的使用場景==

在編寫相對比較公用的程式碼時,優先考慮過濾器,然後是攔截器,最後是aop。

比如:

許可權校驗,一般情況下,所有的請求都需要做登陸校驗,此時就應該使用過濾器在最頂層做校驗;

日誌記錄,一般日誌只會針對部分邏輯做日誌記錄,而且牽扯到業務邏輯完成前後的日誌記錄,因此使用過濾器不能細緻地劃分模組,此時應該考慮攔截器,

然而攔截器也是依據URL做規則匹配,因此相對來說不夠細緻,因此我們會考慮到使用AOP實現,AOP可以針對程式碼的方法級別做攔截,很適合日誌功能。

寫在最後

最後我也明白了,學習這事不能急躁(心急吃不了熱豆腐),不然就會想我一樣淺嘗輒止,只懂個大概或者是概念,

這是遠遠不夠的,Java的路還很漫長,等待著我們去探索,加油!!!

image-20220204212951463