攔截器和過濾器
本文旨在夯實基礎以及實戰加深理解,目的是更深的理解以便掌握,希望能跟著動手敲一遍,絕對受益匪淺
在本文,我會先給出兩者的區別(理論知識),隨后是兩者各自的實操實現
文章目錄
- 攔截器和過濾器
- 什么是過濾器和攔截器?
- 1.過濾器
- 2.攔截器
- 執行整體流程
- 攔截器和過濾器的區別
- 實操:
- 1.過濾器
- 1.定義日志記錄過濾器類`LogFilter`
- 2.在主啟動類上**添加注解**`@ServletComponentScan`
- 2.攔截器
- 1.創建攔截器
- 2.注冊攔截器
- 測試
- 好了,你學會沒?
什么是過濾器和攔截器?
1.過濾器
首先說下什么是過濾器?
- 是
Java Servlet
規范中定義的標準,是Servlet
一部分,- 配置也是非常簡單,直接實現
javax.servlet.Filter
接口就可以,- 也可以用注解
@WebFilter
對特定的URL
攔截
2.攔截器
那攔截器又是什么東東?
- 是
Spring
框架自身提供的組件,也就是說必須依賴SpringMvc
框架才能使用,是位于Spring
的上下文之中- 是
AOP
的一種實現,支持鏈式調用,通過類HandlerInterceptor
實現多個攔截- 說白了:就是切面編程典型實現,允許程序猿在一些核心業務操作執行的前后插入自定義的邏輯代碼,如日志記錄,權限認證等,而無需修改核心代碼的本身
執行整體流程
我們首先看下過濾器和攔截器在整個流程中的執行順序
解釋說明:(結合上圖)
- Filter 開始:
HTTP
請求進入Tomcat
容器。- Filter 鏈處理:請求會經過所有配置的
Filter
的doFilter()
方法。- 進入
Spring MVC
:請求到達DispatcherServlet
(Spring MVC 的核心控制器)。- Interceptor 開始:
DispatcherServlet
分發請求前,會先執行攔截器鏈的preHandle
方法。- Controller 執行:如果
preHandle
返回true
,請求被分發到對應的Controller
方法執行。- Interceptor 后處理:
Controller
執行完畢后,返回ModelAndView
,然后執行攔截器鏈的postHandle
方法。- 視圖渲染完畢后(或請求處理完成后),執行攔截器鏈的
afterCompletion
方法。- Filter 結束:響應最終再次經過 Filter 鏈的
doFilter()
方法(反向),返回給客戶端。
攔截器和過濾器的區別
特性 | 過濾器 (Filter) | 攔截器 (Interceptor) |
---|---|---|
歸屬與依賴 | Servlet 規范,不依賴 Spring | Spring 框架 的組件 |
作用范圍 | 作用于所有進入容器的請求(包括靜態資源,如 /css/ , /js/ ) | 只作用于 Spring MVC 處理的請求(即 Controller 的請求,通常不會攔截靜態資源) |
實現原理 | 基于 函數回調 (doFilter() ) | 基于 Java 反射和動態代理 |
使用方式 | 在 web.xml 中配置或使用 @WebFilter 注解 | 在 Spring MVC 配置中注冊 (WebMvcConfigurer ) |
獲取 IOC 容器 | 無法 直接獲取 Spring 的 IOC 容器和其中的 Bean | 可以,因為它本身就是 Spring 管理的,可以輕松使用 @Autowired 注入其他 Bean |
執行時機/位置 | 最早。 1. 在請求進入 Tomcat 等容器之后 2. 在進入 Servlet 之前 3. 在 Servlet 處理完之后,返回給客戶端之前 | 較晚。 1. 在請求進入 DispatcherServlet 之后 2. 在進入 Controller 之前 (preHandle) 3. 在 Controller 執行之后,視圖渲染之前 (postHandle) 4. 在整個請求結束之后 (afterCompletion) |
功能側重 | 更底層,關注的是請求本身(Request/Response),功能強大。 例如:字符編碼設置、權限校驗(粗粒度)、日志記錄、性能監控、解壓/gzip壓縮。 | 更上層,關注的是 Controller 的方法執行,與業務結合更緊密。 例如:更精細的權限校驗(判斷Token、角色)、日志記錄(記錄Controller方法、參數)、參數預處理、Controller 執行時間計算。 |
實操:
1.過濾器
案例: 在Springboot
中實現日志記錄過濾器
1.定義日志記錄過濾器類LogFilter
主要的三大核心方法:(可以結合上面的整體執行流程圖來看)
init()
:該方法在容器啟動初始化過濾器時被調用,在Filter
的整個生命周期只會被調用一次。注意:這個方法必須執行成功,否則過濾器不生效,沒有作用。doFilter()
:容器中的每一次請求都會調用該方法,FilterChain
用來調用下一個過濾器Filter
。destroy()
: 當容器銷毀 過濾器實例時調用該方法,一般在方法中銷毀或關閉資源,在過濾器Filter
的整個生命周期也只會被調用一次
package com.zhengqian.test01aop.filter;import lombok.extern.slf4j.Slf4j;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;/*** 日志過濾器: * @author zhengqian* @since 2025年09月04日 14:56*/
@WebFilter(urlPatterns = "/*")//過濾所有的請求
@Slf4j
public class LogFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);//初始化執行log.info("***^^^日志過濾器初始化完成");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//記錄請求的開始時間long startTime = System.currentTimeMillis();//轉換為HttpServletRequest獲取更多信息HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;//獲取基本信息String requestURI = httpServletRequest.getRequestURI();String method = httpServletRequest.getMethod();String remoteAddr = httpServletRequest.getRemoteAddr();//log.info("請求開始=> 方法:{} 路徑: {}} IP:{}",method,requestURI,remoteAddr);try {filterChain.doFilter(servletRequest,servletResponse);} finally {//計算處理耗時long duration = System.currentTimeMillis() - startTime;//響應日志log.info("請求結束 ==> 路徑: {} 耗時:{}",requestURI,duration);}}@Overridepublic void destroy() {Filter.super.destroy();log.info("日志過濾器銷毀");}
}
2.在主啟動類上添加注解@ServletComponentScan
OK!這個時候你啟動就可以看到此過濾器成功!
你可能會問?這么簡單?對的,就是這么簡單!前提你要跟著做!哈哈哈!
2.攔截器
1.創建攔截器
核心方法解釋:
preHandle()
:
- 此方法在請求處理之前進行調用。
- 注意:如果該方法的返回值為
false
,將視為當前請求結束,不僅自身的攔截器會失效,還會導致其他的攔截器也不再執行。postHandle()
:
- 只有在
preHandle()
方法返回值為true
時才會執行。- 會在
Controller
中的方法調用之后,DispatcherServlet
返回渲染視圖之前被調用。afterCompletion()
:只有在preHandle()
方法返回值為true
時才會執行。在整個請求結束之后,DispatcherServlet
渲染了對應的視圖之后執行。
package com.zhengqian.test01aop.interceptor;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 自定義攔截器的實現* @author zhengqian* @since 2025年09月04日 15:13*/
@Slf4j
public class CustomInterceptor implements HandlerInterceptor {/*** 請求處理前被調用* @param request 請求* @param response 響應* @param handler 處理器* @return aa* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//添加校驗權限log.info("開始出發攔截器校驗前的處理------------->");return true;}/*** **其中**:`postHandle()` 方法被調用的順序跟 `preHandle()` 是**相反**的,先聲明的攔截器 `preHandle()` 方法先執行,而`postHandle()`方法反而會后執行。* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("只有在 preHandle() 方法返回值為true 時才會執行");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("整個請求結束之后被調用");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}}
2.注冊攔截器
addPathPatterns
:需要攔截的路徑
excludePathPatterns
: 不需要攔截的路徑
package com.zhengqian.test01aop.config;import com.zhengqian.test01aop.interceptor.CustomInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author zhengqian* @since 2025年09月04日 15:19*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注冊攔截器并設置攔截路徑registry.addInterceptor(new CustomInterceptor())//攔截所有.addPathPatterns("/**")//排除*/login的登錄路徑.excludePathPatterns("/aop/login");}
}
測試
我們建一個測試類,來進行上面的攔截測試
package com.zhengqian.test01aop.controller;import com.zhengqian.test01aop.service.TestService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @author zhengqian* @date 2025年07月21日 16:40*/
@RestController
@RequestMapping("/aop")
public class TestController {@ResourceTestService testService;@GetMapping("/test")public String test(){testService.test();return "測試成功!----";}@GetMapping("/login")public String login(){testService.test();return "測試成功!----";}
}
打印日志:
1.走攔截器