最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中過(guò)濾器(Filter)和攔截器(Interceptor)到底啥區(qū)別詳解

 更新時(shí)間:2026年04月10日 11:16:05   作者:木木一直在哭泣  
在Spring框架中過(guò)濾器(Filter)和攔截器(Interceptor)都是用于處理HTTP請(qǐng)求的重要組件,但它們的工作原理和執(zhí)行順序有所不同,這篇文章主要介紹了Spring中過(guò)濾器(Filter)和攔截器(Interceptor)到底啥區(qū)別的相關(guān)資料,需要的朋友可以參考下

前言

在做 Spring Boot 開(kāi)發(fā)時(shí),經(jīng)常會(huì)聽(tīng)到兩句話(huà):“用 Filter 做統(tǒng)一處理”、“用 Interceptor 攔截請(qǐng)求”。很多同學(xué)會(huì)混淆:它們是不是都是 Spring 的?它們?cè)谡?qǐng)求鏈路的哪個(gè)位置?適合干嘛?Spring Boot 里怎么寫(xiě)?

這篇文章用簡(jiǎn)單的方式講清楚 Filter 和 Interceptor 的定位,并給出兩個(gè)能直接復(fù)制運(yùn)行的例子,最后用一個(gè)總結(jié)把選擇建議說(shuō)透。

1. 先下結(jié)論:它們歸屬不同

Filter(過(guò)濾器)不是 Spring 發(fā)明的,它屬于 Servlet 規(guī)范,由 Tomcat/Jetty 等容器負(fù)責(zé)調(diào)用。它的位置更靠外,在請(qǐng)求進(jìn)入 Spring MVC 之前就會(huì)執(zhí)行。

Interceptor(攔截器)通常指 Spring MVC 的 HandlerInterceptor,是 Spring MVC 的能力。它的位置更靠?jī)?nèi),請(qǐng)求進(jìn)入 DispatcherServlet 之后、Controller 方法執(zhí)行前后才會(huì)觸發(fā)。

2. 用“洋蔥模型”理解位置關(guān)系

請(qǐng)求從外往內(nèi)走:

Tomcat → Filter(Servlet 規(guī)范)→ DispatcherServlet(Spring MVC 入口)→ Interceptor(Spring MVC)→ Controller

響應(yīng)返回時(shí),再按相反方向回去。

3. 什么時(shí)候用 Filter?什么時(shí)候用 Interceptor?

Filter 更適合做“全站通用、跟業(yè)務(wù)無(wú)關(guān)”的事情,例如編碼處理、全局 CORS、XSS 過(guò)濾、RequestWrapper(讀 body/改 header)、統(tǒng)一訪問(wèn)日志、限流、以及安全鏈路相關(guān)處理。很多人不知道的是,Spring Security 本質(zhì)上就是一條很長(zhǎng)的 FilterChain。

Interceptor 更適合做“跟 Controller/業(yè)務(wù)強(qiáng)相關(guān)”的事情,例如登錄態(tài)/權(quán)限校驗(yàn)(尤其需要拿到 Controller 方法或注解時(shí))、接口埋點(diǎn)統(tǒng)計(jì)每個(gè) Controller 的耗時(shí)、統(tǒng)一注入上下文(userId、traceId)并在 afterCompletion 清理 ThreadLocal/MDC、多租戶(hù) tenant 上下文等。

4. 最關(guān)鍵區(qū)別:觸發(fā)時(shí)機(jī) & 能拿到的信息

維度FilterInterceptor
歸屬Servlet 規(guī)范(容器調(diào)用)Spring MVC(DispatcherServlet 調(diào)用)
執(zhí)行時(shí)機(jī)更早(進(jìn)入 Spring MVC 之前)更晚(進(jìn)入 Spring MVC 之后,Controller 前后)
是否能拿到 Controller 方法不知道具體 handler能拿到 handler(方法/注解)
典型用途全局通用處理、安全過(guò)濾、包裝 request業(yè)務(wù)校驗(yàn)、埋點(diǎn)、上下文管理、權(quán)限

5. 例子 1:Filter 記錄全站耗時(shí)(完整可用)

目標(biāo)是打印每個(gè)請(qǐng)求的 URI、線程名、耗時(shí),并且能直觀看到它在 Controller 前后執(zhí)行。推薦繼承 OncePerRequestFilter,避免一次請(qǐng)求多次執(zhí)行的問(wèn)題。

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class AccessLogFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
    ) throws ServletException, IOException {
        long start = System.currentTimeMillis();
        String uri = request.getRequestURI();
        String thread = Thread.currentThread().getName();
        try {
            System.out.println("[Filter] before chain, uri=" + uri + ", thread=" + thread);
            filterChain.doFilter(request, response); // 放行,進(jìn)入 DispatcherServlet / Controller
        } finally {
            long cost = System.currentTimeMillis() - start;
            System.out.println("[Filter] after chain , uri=" + uri + ", cost=" + cost + "ms");
        }
    }
}

理解要點(diǎn)是:filterChain.doFilter 前是“進(jìn) Spring MVC 之前”,doFilter 后是“Controller 和后續(xù)處理都結(jié)束后”。

6. 例子 2:Interceptor 統(tǒng)一注入 userId + traceId,并正確清理(完整可用)

這個(gè)例子更貼近真實(shí)線上:從請(qǐng)求頭/參數(shù)拿 userId,生成 traceId,放到 ThreadLocal,Controller 里隨時(shí)能取,同時(shí)在 afterCompletion 清理,避免線程池復(fù)用導(dǎo)致串號(hào)和“線程級(jí)常駐”。

先定義一個(gè)上下文對(duì)象和 ThreadLocal 容器。

RequestContext.java:

public class RequestContext {
    private final String userId;
    private final String traceId;
    public RequestContext(String userId, String traceId) {
        this.userId = userId;
        this.traceId = traceId;
    }
    public String getUserId() { return userId; }
    public String getTraceId() { return traceId; }
}

RequestContextHolder.java:

public class RequestContextHolder {
    private static final ThreadLocal<RequestContext> CTX = new ThreadLocal<>();
    public static void set(RequestContext ctx) { CTX.set(ctx); }
    public static RequestContext get() { return CTX.get(); }
    public static void remove() { CTX.remove(); }
}

然后實(shí)現(xiàn) Interceptor。

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.UUID;
public class ContextInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String userId = request.getHeader("X-UserId");
        if (userId == null || userId.isBlank()) {
            userId = request.getParameter("userId");
        }
        if (userId == null || userId.isBlank()) {
            userId = "anonymous";
        }
        String traceId = UUID.randomUUID().toString().replace("-", "");
        RequestContextHolder.set(new RequestContext(userId, traceId));
        System.out.println("[Interceptor] preHandle, userId=" + userId + ", traceId=" + traceId);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        RequestContextHolder.remove();
        System.out.println("[Interceptor] afterCompletion, cleaned");
    }
}

接著在 Spring Boot 中注冊(cè) Interceptor。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ContextInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/health");
    }
}

最后寫(xiě)一個(gè) Controller 驗(yàn)證上下文是否能拿到。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
    @GetMapping("/demo")
    public String demo() {
        RequestContext ctx = RequestContextHolder.get();
        return "ok, userId=" + ctx.getUserId() + ", traceId=" + ctx.getTraceId();
    }
}

訪問(wèn) /demo?userId=mm,你會(huì)看到輸出順序大致是:

[Filter] before chain
[Interceptor] preHandle
Controller 執(zhí)行
[Interceptor] afterCompletion
[Filter] after chain

這也能直觀看到 Filter 在外層包著整個(gè) Spring MVC,而 Interceptor 在 Spring MVC 內(nèi)部圍繞 Controller。

總結(jié)

Filter 屬于 Servlet 規(guī)范,位置更靠外,更適合做全站通用處理(日志、編碼、CORS、安全過(guò)濾、請(qǐng)求包裝等),它只關(guān)心 request/response,不知道具體會(huì)走哪個(gè) Controller。

Interceptor 屬于 Spring MVC,位置更靠?jī)?nèi),更適合做與 Controller/業(yè)務(wù)相關(guān)的攔截(權(quán)限、埋點(diǎn)、上下文注入與清理、多租戶(hù)等),它能拿到 handler(方法/注解),并且可以在 afterCompletion 做統(tǒng)一清理。

寫(xiě) Filter 記住圍繞 chain.doFilter 包一層;寫(xiě) Interceptor 記住 preHandle 做攔截、afterCompletion 做清理(尤其 ThreadLocal/MDC)。

到此這篇關(guān)于Spring中過(guò)濾器(Filter)和攔截器(Interceptor)到底啥區(qū)別的文章就介紹到這了,更多相關(guān)Spring過(guò)濾器Filter和攔截器Interceptor內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • ShardingSphere jdbc實(shí)現(xiàn)分庫(kù)分表核心概念詳解

    ShardingSphere jdbc實(shí)現(xiàn)分庫(kù)分表核心概念詳解

    這篇文章主要為大家介紹了ShardingSphere jdbc實(shí)現(xiàn)分庫(kù)分表核心概念詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Java?設(shè)計(jì)模式中的命令模式詳情

    Java?設(shè)計(jì)模式中的命令模式詳情

    這篇文章主要介紹了Java?設(shè)計(jì)模式中的命令模式詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-07-07
  • 使用Java DOM解析器修改XML文件內(nèi)容的操作方法

    使用Java DOM解析器修改XML文件內(nèi)容的操作方法

    在Java中,XML文件的解析和修改可以通過(guò)多種方法實(shí)現(xiàn),其中DOM(Document Object Model)是一種常用的方式,在本文中,我們將介紹如何使用Java DOM解析器修改XML文件中的內(nèi)容,并給出一個(gè)具體的示例,需要的朋友可以參考下
    2024-08-08
  • Spring?Retry實(shí)現(xiàn)重試機(jī)制的示例詳解

    Spring?Retry實(shí)現(xiàn)重試機(jī)制的示例詳解

    這篇文章主要為大家詳細(xì)介紹了Spring-Retry的用法以及實(shí)現(xiàn)原理是怎么樣的,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下
    2023-07-07
  • 用Java打印九九除法表代碼分析

    用Java打印九九除法表代碼分析

    這篇文章主要介紹了如何用Java語(yǔ)言打印九九除法表,包括其使用的源代碼,需要的朋友可以參考下。
    2017-08-08
  • SpringCloud之Zuul網(wǎng)關(guān)原理及其配置講解

    SpringCloud之Zuul網(wǎng)關(guān)原理及其配置講解

    這篇文章主要介紹了SpringCloud之Zuul網(wǎng)關(guān)原理及其配置講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 淺談Storm在zookeeper上的目錄結(jié)構(gòu)

    淺談Storm在zookeeper上的目錄結(jié)構(gòu)

    這篇文章主要介紹了淺談Storm在zookeeper上的目錄結(jié)構(gòu)的相關(guān)內(nèi)容,涉及storm使用zookeeper的操作以及詳細(xì)結(jié)構(gòu)圖,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-10-10
  • Java循環(huán)對(duì)bean的屬性進(jìn)行賦值的實(shí)現(xiàn)

    Java循環(huán)對(duì)bean的屬性進(jìn)行賦值的實(shí)現(xiàn)

    本文主要介紹了Java循環(huán)對(duì)bean的屬性進(jìn)行賦值,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java 對(duì)稱(chēng)二叉樹(shù)的判斷

    java 對(duì)稱(chēng)二叉樹(shù)的判斷

    這篇文章主要介紹了java 對(duì)稱(chēng)二叉樹(shù)的判斷,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Spring中的EurekaServer啟動(dòng)詳解

    Spring中的EurekaServer啟動(dòng)詳解

    這篇文章主要介紹了Spring中的EurekaServer啟動(dòng)詳解,初始化eureka,包含eureka集群的同步和發(fā)布注冊(cè),這個(gè)方法時(shí)重寫(xiě)ServletContextListener#contextInitialized,是eureka啟動(dòng)的入口了,需要的朋友可以參考下
    2023-11-11

最新評(píng)論

成武县| 呼伦贝尔市| 贡嘎县| 拜城县| 中山市| 桓台县| 玉屏| 景东| 龙山县| 壶关县| 潜江市| 怀远县| 滨州市| 尼勒克县| 灵山县| 河曲县| 新乡市| 翁源县| 东乌珠穆沁旗| 通榆县| 磐石市| 许昌市| 湛江市| 湖口县| 彭州市| 浦东新区| 高密市| 叙永县| 桂阳县| 多伦县| 威远县| 莲花县| 华容县| 抚顺市| 宾阳县| 库尔勒市| 郁南县| 布拖县| 普陀区| 安陆市| 荆州市|