Java Web過濾器的核心原理、實(shí)現(xiàn)與執(zhí)行順序配置方法(最新整理)
前言
過濾器是Java EE標(biāo)準(zhǔn)中Servlet規(guī)范提供的功能,也是傳統(tǒng)JavaWeb的三大組件之一(Servlet,F(xiàn)ilter和Listener)。通過過濾器可以把所有進(jìn)入Servlet容器的請(qǐng)求都攔截住,無(wú)論靜態(tài)資源還是Controller,從而實(shí)現(xiàn)一些通用的操作。
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
一、核心功能
自定義過濾器的核心能力來(lái)自Filter這個(gè)接口。這個(gè)接口分別有init(),doFilter()和destroy()方法。init和destroy的生命周期依托于Servlet容器,容器初始化的時(shí)候會(huì)執(zhí)行init()方法,容器銷毀的時(shí)候會(huì)執(zhí)行destroy()方法。
Filter 接口
package jakarta.servlet;
import java.io.IOException;
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}其中最常用的是doFilter方法,在每一次請(qǐng)求管道中,都會(huì)執(zhí)行我們重寫的這個(gè)方法。doFilter有一核心的FilterChain參數(shù),它的核心作用是將請(qǐng)求傳遞給下一個(gè) Filter或最終的控制器。缺少這個(gè)調(diào)用時(shí),請(qǐng)求會(huì)卡在當(dāng)前 Filter,后續(xù)所有處理環(huán)節(jié)都不會(huì)執(zhí)行。
比如下面的doFilter方法中包含了對(duì)filterChain的調(diào)用,這樣請(qǐng)求才會(huì)傳遞到后面,否則就會(huì)中斷。
filterChain.doFilter(servletRequest, servletResponse);
另外doFilter方法中還有ServletRequest和ServletResponse這兩個(gè)參數(shù),這兩個(gè)分別對(duì)應(yīng)請(qǐng)求和響應(yīng)。我們可以通過這兩個(gè)參數(shù)來(lái)查看或設(shè)置請(qǐng)求/響應(yīng)
二、實(shí)現(xiàn)方式
過濾器本身是Servlet規(guī)范提供的功能,在Spring Boot中實(shí)現(xiàn)一個(gè)過濾器主要通過兩種方法,接下來(lái)通過兩種方式實(shí)現(xiàn)一個(gè)字符格式化過濾器和日志打印過濾器。
以下兩種寫法其中本質(zhì)上還是Spring Boot默認(rèn)使用FilterRegistrationBean來(lái)注冊(cè)自定義Filter到容器中,兩者僅僅是寫法上不同
2.1 @WebFilter + @ServletComponentScan
首先我們定義一個(gè)CharsetFilter類來(lái)實(shí)現(xiàn)Filter類,F(xiàn)ilter功能由jakarta.servlet這個(gè)包提供提供。
注意在自定義類上要使用@WebFilter()注解修飾,里面的參數(shù)則是URI路徑匹配通配符,這里設(shè)置為"/*",表示匹配所有。
CharsetFilter類主要是攔截住請(qǐng)求,該Filter接口里最為核心的方法是doFilter。我們?cè)赿oFilter的重載里寫上自定義的邏輯,比如這里是將客戶端到服務(wù)器和服務(wù)器到客戶端的數(shù)據(jù)流都設(shè)置為UTF-8的編碼格式。
值得注意的是@WebFilter需要和@ServletComponentScan搭配使用。需要在啟動(dòng)類上通過@ServletComponentScan來(lái)控制Spring Boot啟動(dòng)的時(shí)候掃描到這個(gè)被@WebFilter修飾的自定義過濾器。
通過@WebFilter + @ServletComponentScan這種實(shí)現(xiàn)本質(zhì)上是Spring引導(dǎo) Servlet 容器掃描@WebFilter最終由Servlet 容器直接調(diào)用ServletContext.addFilter()完成過濾器的注冊(cè)。
package org.araby.blognovelink.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
@WebFilter("/*")
public class CharsetFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("初始化統(tǒng)一字符編碼過濾器");
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("統(tǒng)一字符編碼過濾器開始執(zhí)行");
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setCharacterEncoding("UTF-8");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
log.info("銷毀統(tǒng)一字符編碼過濾器");
Filter.super.destroy();
}
}@SpringBootApplication
@ServletComponentScan
public class BlogNovelinkApplication {
public static void main(String[] args) {
SpringApplication.run(BlogNovelinkApplication.class, args);
}
}
執(zhí)行結(jié)果里包含

2.2 @Component修飾自定義過濾器類
第二種通過在自定義過濾器類添加@Component會(huì)更加的簡(jiǎn)單,這樣就不需要在啟動(dòng)類上添加@ServletComponentScan。這種方式基于Spring的IOC容器,直接將過濾器作為Bean對(duì)象注冊(cè)到容器中,最后在請(qǐng)求管道里注入自定義過濾器。
但僅用@Component注解標(biāo)記Filter類時(shí),Spring Boot 會(huì)自動(dòng)注冊(cè)該過濾器,默認(rèn)攔截規(guī)則為所有請(qǐng)求。如果要手動(dòng)控制匹配順序,需要手動(dòng)實(shí)現(xiàn)一個(gè)配置類來(lái)實(shí)現(xiàn)FilterRegistrationBean的注冊(cè),這里后面再詳細(xì)介紹。
這里我們定義一個(gè)自定義的日志過濾器,這次只有在類上加一個(gè)@Component注解。其底層還是依賴 Spring的FilterRegistrationBean完成注冊(cè)。
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
@Component
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("初始化日志過濾器");
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("日志過濾器開始執(zhí)行");
HttpServletRequest request = (HttpServletRequest) servletRequest;
String uri = request.getRequestURI();
String method = request.getMethod();
String ip = request.getRemoteAddr();
long startTime = System.currentTimeMillis();
filterChain.doFilter(servletRequest, servletResponse);
// 記錄響應(yīng)耗時(shí)
long costTime = System.currentTimeMillis() - startTime;
log.info("Response - URL: {}, Cost Time: {}ms", uri, costTime);
}
@Override
public void destroy() {
log.info("銷毀日志過濾器");
Filter.super.destroy();
}
}三、過濾器執(zhí)行的順序
3.1 現(xiàn)象討論
假想一下有三個(gè)過濾器在請(qǐng)求管道里,分別是過濾器a,過濾器b和過濾器c。它們都是通過@WebFilter + @ServletComponentScan的方式初始化。
觀察執(zhí)行結(jié)果發(fā)現(xiàn)了一個(gè)神奇的現(xiàn)象,初始化和銷毀順序 不等于執(zhí)行順序。初始化/銷毀順序是A-C-B,而 執(zhí)行順序是A-B-C。這是因?yàn)锧WebFilter + @ServletComponentScan默認(rèn)情況下自定義過濾器其初始化順序由Servlet容器加載Servlet組件的規(guī)則決定,而執(zhí)行順序由Servlet容器構(gòu)建FilterChain時(shí)的排序規(guī)則,像Tomcat默認(rèn)會(huì)對(duì)所有@WebFilter按類名排序。
執(zhí)行結(jié)果
[INFO ] [2025-12-02 22:14:18.365] [main] [] o.araby.blognovelink.filter.AFilter - 初始化A過濾器
[INFO ] [2025-12-02 22:14:18.365] [main] [] o.araby.blognovelink.filter.CFilter - 初始化C過濾器
[INFO ] [2025-12-02 22:14:18.365] [main] [] o.araby.blognovelink.filter.BFilter - 初始化B過濾器
[INFO ] [2025-12-02 22:14:36.764] [http-nio-9090-exec-2] [] o.araby.blognovelink.filter.AFilter - A過濾器開始執(zhí)行
[INFO ] [2025-12-02 22:14:36.765] [http-nio-9090-exec-2] [] o.araby.blognovelink.filter.BFilter - B過濾器開始執(zhí)行
[INFO ] [2025-12-02 22:14:36.765] [http-nio-9090-exec-2] [] o.araby.blognovelink.filter.CFilter - C過濾器開始執(zhí)行
[INFO ] [2025-12-02 22:14:42.262] [SpringApplicationShutdownHook] [] o.araby.blognovelink.filter.AFilter - 銷毀A過濾器
[INFO ] [2025-12-02 22:14:42.262] [SpringApplicationShutdownHook] [] o.araby.blognovelink.filter.CFilter - 銷毀C過濾器
[INFO ] [2025-12-02 22:14:42.262] [SpringApplicationShutdownHook] [] o.araby.blognovelink.filter.BFilter - 銷毀B過濾器
3.2 自定義順序
既然都已經(jīng)是在Spring Boot中使用過濾器,那么我們就應(yīng)該用Spring的方式管理Filter,而不是依賴 Servlet容器的隱式行為。這里其實(shí)是采用FilterRegistrationBean來(lái)手動(dòng)定義注冊(cè)的順序,所以過濾器類可以不用@WebFilter + @ServletComponentScan或者@Component修飾。啟動(dòng)類上也不需要@ServletComponentScan。
這樣注冊(cè)過濾器的的過程由Spring Boot通過FilterRegistrationBean統(tǒng)一管理,而非直接依賴Servlet 容器的注解掃描。
這里我們定義一個(gè)配置類WebConfig ,用@Configuration注解修飾。里面手動(dòng)實(shí)現(xiàn)FilterRegistrationBean來(lái)管理定義的過濾器。
- setFilter:添加過濾器類
- addUrlPatterns:添加匹配路徑
- setOrder:指定順序,數(shù)值越小,過濾器doFilter越先執(zhí)行
import org.araby.blognovelink.filter.AFilter;
import org.araby.blognovelink.filter.BFilter;
import org.araby.blognovelink.filter.CFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<AFilter> aFilter() {
FilterRegistrationBean<AFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new AFilter());
bean.addUrlPatterns("/*");
bean.setOrder(1); // 最先執(zhí)行
return bean;
}
@Bean
public FilterRegistrationBean<BFilter> bFilter() {
FilterRegistrationBean<BFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new BFilter());
bean.addUrlPatterns("/*");
bean.setOrder(2);
return bean;
}
@Bean
public FilterRegistrationBean<CFilter> cFilter() {
FilterRegistrationBean<CFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new CFilter());
bean.addUrlPatterns("/*");
bean.setOrder(3); // 最后執(zhí)行
return bean;
}
}到此這篇關(guān)于Java Web過濾器的核心原理、實(shí)現(xiàn)與執(zhí)行順序配置的文章就介紹到這了,更多相關(guān)javaweb過濾器原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot整合liteflow的實(shí)現(xiàn)示例
本文主要介紹了在Spring Boot項(xiàng)目中整合Liteflow規(guī)則引擎,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
MyBatis trim標(biāo)簽核心用法代碼實(shí)戰(zhàn)
MyBatis的<trim>標(biāo)簽用于動(dòng)態(tài)SQL拼接,處理SQL前后綴和多余關(guān)鍵字,避免語(yǔ)法錯(cuò)誤,提升代碼可維護(hù)性,本文給大家介紹MyBatis trim標(biāo)簽核心用法代碼實(shí)戰(zhàn),感興趣的朋友跟隨小編一起看看吧2026-03-03
maven使用systemPath方式加載本地jar的實(shí)現(xiàn)
本文介紹Maven通過systemPath加載本地第三方JAR的方法,需配置依賴和插件以確保打包和運(yùn)行,具有一定的參考價(jià)值,感興趣的可以了解一下2025-06-06
Spring?中?PageHelper?不生效問題及解決方法
這篇文章主要介紹了Spring?中?PageHelper?不生效問題,使用這個(gè)插件時(shí)要注意版本的問題,不同的版本可能 PageHelper 不會(huì)生效,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
Java實(shí)現(xiàn)動(dòng)態(tài)生成GIF圖像詳解
在互聯(lián)網(wǎng)上有許多有趣的場(chǎng)景,其中的一種就是動(dòng)圖。這不是視頻,而是一種GIF圖像信息。本文將利用Java實(shí)現(xiàn)動(dòng)態(tài)生成GIF圖像功能,需要的可以參考一下2022-09-09
SpringMVC使用@ExceptionHandler注解在Controller中處理異常
這篇文章主要為大家介紹了SpringMVC使用@ExceptionHandler注解在Controller中處理異常示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10

