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容器的請求都攔截住,無論靜態(tài)資源還是Controller,從而實(shí)現(xiàn)一些通用的操作。
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
一、核心功能
自定義過濾器的核心能力來自Filter這個(gè)接口。這個(gè)接口分別有init(),doFilter()和destroy()方法。init和destroy的生命周期依托于Servlet容器,容器初始化的時(shí)候會執(zhí)行init()方法,容器銷毀的時(shí)候會執(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方法,在每一次請求管道中,都會執(zhí)行我們重寫的這個(gè)方法。doFilter有一核心的FilterChain參數(shù),它的核心作用是將請求傳遞給下一個(gè) Filter或最終的控制器。缺少這個(gè)調(diào)用時(shí),請求會卡在當(dāng)前 Filter,后續(xù)所有處理環(huán)節(jié)都不會執(zhí)行。
比如下面的doFilter方法中包含了對filterChain的調(diào)用,這樣請求才會傳遞到后面,否則就會中斷。
filterChain.doFilter(servletRequest, servletResponse);
另外doFilter方法中還有ServletRequest和ServletResponse這兩個(gè)參數(shù),這兩個(gè)分別對應(yīng)請求和響應(yīng)。我們可以通過這兩個(gè)參數(shù)來查看或設(shè)置請求/響應(yīng)
二、實(shí)現(xiàn)方式
過濾器本身是Servlet規(guī)范提供的功能,在Spring Boot中實(shí)現(xiàn)一個(gè)過濾器主要通過兩種方法,接下來通過兩種方式實(shí)現(xiàn)一個(gè)字符格式化過濾器和日志打印過濾器。
以下兩種寫法其中本質(zhì)上還是Spring Boot默認(rèn)使用FilterRegistrationBean來注冊自定義Filter到容器中,兩者僅僅是寫法上不同
2.1 @WebFilter + @ServletComponentScan
首先我們定義一個(gè)CharsetFilter類來實(shí)現(xiàn)Filter類,F(xiàn)ilter功能由jakarta.servlet這個(gè)包提供提供。
注意在自定義類上要使用@WebFilter()注解修飾,里面的參數(shù)則是URI路徑匹配通配符,這里設(shè)置為"/*",表示匹配所有。
CharsetFilter類主要是攔截住請求,該Filter接口里最為核心的方法是doFilter。我們在doFilter的重載里寫上自定義的邏輯,比如這里是將客戶端到服務(wù)器和服務(wù)器到客戶端的數(shù)據(jù)流都設(shè)置為UTF-8的編碼格式。
值得注意的是@WebFilter需要和@ServletComponentScan搭配使用。需要在啟動類上通過@ServletComponentScan來控制Spring Boot啟動的時(shí)候掃描到這個(gè)被@WebFilter修飾的自定義過濾器。
通過@WebFilter + @ServletComponentScan這種實(shí)現(xiàn)本質(zhì)上是Spring引導(dǎo) Servlet 容器掃描@WebFilter最終由Servlet 容器直接調(diào)用ServletContext.addFilter()完成過濾器的注冊。
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會更加的簡單,這樣就不需要在啟動類上添加@ServletComponentScan。這種方式基于Spring的IOC容器,直接將過濾器作為Bean對象注冊到容器中,最后在請求管道里注入自定義過濾器。
但僅用@Component注解標(biāo)記Filter類時(shí),Spring Boot 會自動注冊該過濾器,默認(rèn)攔截規(guī)則為所有請求。如果要手動控制匹配順序,需要手動實(shí)現(xiàn)一個(gè)配置類來實(shí)現(xiàn)FilterRegistrationBean的注冊,這里后面再詳細(xì)介紹。
這里我們定義一個(gè)自定義的日志過濾器,這次只有在類上加一個(gè)@Component注解。其底層還是依賴 Spring的FilterRegistrationBean完成注冊。
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è)過濾器在請求管道里,分別是過濾器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)會對所有@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來手動定義注冊的順序,所以過濾器類可以不用@WebFilter + @ServletComponentScan或者@Component修飾。啟動類上也不需要@ServletComponentScan。
這樣注冊過濾器的的過程由Spring Boot通過FilterRegistrationBean統(tǒng)一管理,而非直接依賴Servlet 容器的注解掃描。
這里我們定義一個(gè)配置類WebConfig ,用@Configuration注解修飾。里面手動實(shí)現(xiàn)FilterRegistrationBean來管理定義的過濾器。
- 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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java類Circle定義計(jì)算圓的面積和周長代碼示例
要用Java計(jì)算圓的周長和面積,需要使用圓的半徑和一些數(shù)學(xué)公式,下面這篇文章主要給大家介紹了關(guān)于java類Circle定義計(jì)算圓的面積、周長的相關(guān)資料,需要的朋友可以參考下2024-04-04
Java終止循環(huán)體的具體實(shí)現(xiàn)
這篇文章主要介紹了Java終止循環(huán)體的具體實(shí)現(xiàn),需要的朋友可以參考下2014-02-02
SpringBoot項(xiàng)目保證接口冪等的五種方法
在計(jì)算機(jī)領(lǐng)域中,冪等是指任意一個(gè)操作的多次執(zhí)行總是能獲得相同的結(jié)果,不會對系統(tǒng)狀態(tài)產(chǎn)生額外影響,在Java后端開發(fā)中,冪等性的實(shí)現(xiàn)通常通過確保方法或服務(wù)調(diào)用的結(jié)果具有確定性,本文給大家介紹了SpringBoot項(xiàng)目保證接口冪等的五種方法,需要的朋友可以參考下2025-07-07
Java設(shè)計(jì)模式之策略模式(Strategy模式)介紹
這篇文章主要介紹了Java設(shè)計(jì)模式之策略模式(Strategy模式)介紹,Strategy是屬于設(shè)計(jì)模式中對象行為型模式,要是定義一系列的算法,這些算法一個(gè)個(gè)封裝成單獨(dú)的類,需要的朋友可以參考下2015-03-03
IDEA JeeSite框架httpSession.invalidate()無效問題解決方案
這篇文章主要介紹了IDEA JeeSite框架httpSession.invalidate()無效問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java三種方法將List轉(zhuǎn)換為Map的實(shí)例
今天小編就為大家分享一篇關(guān)于Java三種方法將List轉(zhuǎn)換為Map的實(shí)例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10
Java Integer.valueOf()和Integer.parseInt()的區(qū)別說明
這篇文章主要介紹了Java Integer.valueOf()和Integer.parseInt()的區(qū)別說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

