OncePerRequestFilter和GenericFilterBean的區(qū)別及說明
OncePerRequestFilter和GenericFilterBean過濾器的區(qū)別
1. 繼承關(guān)系與核心功能
GenericFilterBean
- 是 Spring 對
javax.servlet.Filter接口的實(shí)現(xiàn),提供基礎(chǔ)功能(如初始化、銷毀方法的默認(rèn)實(shí)現(xiàn))。 - 需要開發(fā)者手動(dòng)控制過濾邏輯的執(zhí)行次數(shù),無法自動(dòng)確保每個(gè)請求只執(zhí)行一次。
- 適用于需要靈活控制執(zhí)行次數(shù)的場景(如多次記錄日志或動(dòng)態(tài)處理請求)。
OncePerRequestFilter
- 繼承自
GenericFilterBean,核心功能是確保每個(gè) HTTP 請求的過濾邏輯僅執(zhí)行一次。 - 內(nèi)部通過檢查請求屬性(如
javax.servlet._FILTERED)或ThreadLocal來跟蹤請求是否已處理過。 - 適用于需要嚴(yán)格保證單次執(zhí)行的場景(如認(rèn)證、鑒權(quán)、流量控制等)。
2. 執(zhí)行邏輯的區(qū)別
| 特性 | GenericFilterBean | OncePerRequestFilter |
|---|---|---|
| 執(zhí)行次數(shù)控制 | 需手動(dòng)確保邏輯不重復(fù)執(zhí)行(如請求轉(zhuǎn)發(fā)時(shí)可能多次觸發(fā))。 | 自動(dòng)確保每個(gè)請求僅執(zhí)行一次,即使請求被轉(zhuǎn)發(fā)或異步處理。 |
| 適用場景 | 需要多次執(zhí)行的邏輯(如日志記錄、動(dòng)態(tài)參數(shù)處理)。 | 需要單次執(zhí)行的邏輯(如認(rèn)證、安全校驗(yàn))。 |
| 需重寫的方法 | doFilter | doFilterInternal |
| 依賴Servlet API | 直接使用Servlet原生方法。 | 內(nèi)部封裝了Servlet邏輯,簡化重復(fù)執(zhí)行的檢查。 |
3. 使用場景對比
選擇 GenericFilterBean 的場景
- 需要對請求進(jìn)行多次攔截(如每個(gè)轉(zhuǎn)發(fā)點(diǎn)都需要記錄日志)。
- 需要細(xì)粒度控制過濾邏輯的執(zhí)行時(shí)機(jī)(如根據(jù)請求路徑動(dòng)態(tài)處理)。
- 示例:請求頭的動(dòng)態(tài)修改、跨域預(yù)檢(OPTIONS 請求)的多次處理。
選擇 OncePerRequestFilter 的場景
- 需要確保邏輯僅執(zhí)行一次(如身份驗(yàn)證、設(shè)置請求上下文)。
- 避免資源重復(fù)消耗(如數(shù)據(jù)庫連接、令牌生成)。
- 示例:JWT 認(rèn)證、Trace ID 的一次設(shè)置。
如何使用?
1. GenericFilterBean 的使用
步驟:
- 繼承
GenericFilterBean。 - 重寫
doFilter方法,實(shí)現(xiàn)自定義邏輯。 - 在 Spring 配置中注冊過濾器(如
WebMvcConfigurer或@Bean)。
示例代碼:記錄每次請求的 URL 和耗時(shí)(可能多次觸發(fā)):
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/*") // 配置過濾路徑
public class LoggingFilter extends GenericFilterBean {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
//請求響應(yīng)前
long startTime = System.currentTimeMillis();
System.out.println("Request Start: " + request.getRequestURI());
chain.doFilter(request, response); // 繼續(xù)處理
//請求響應(yīng)后
long endTime = System.currentTimeMillis();
System.out.println("Request End: " + request.getRequestURI() + ", Time: " + (endTime - startTime) + "ms");
}
}
2. OncePerRequestFilter 的使用
步驟:
- 繼承
OncePerRequestFilter。 - 重寫
doFilterInternal方法,實(shí)現(xiàn)單次執(zhí)行的邏輯。 - 在 Spring 配置中注冊過濾器。
示例代碼:設(shè)置請求頭(僅執(zhí)行一次):
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HeaderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
//請求響應(yīng)前
long startTime = System.currentTimeMillis();
System.out.println("Request Start: " + request.getRequestURI());
// 設(shè)置請求頭,僅執(zhí)行一次
response.setHeader("X-Custom-Header", "Spring-Once");
// 必須調(diào)用 chain.doFilter(),否則請求無法繼續(xù)
chain.doFilter(request, response);
//請求響應(yīng)后
long endTime = System.currentTimeMillis();
System.out.println("Request End: " + request.getRequestURI() + ", Time: " + (endTime - startTime) + "ms");
}
}
關(guān)鍵區(qū)別總結(jié)
| 場景 | GenericFilterBean | OncePerRequestFilter |
|---|---|---|
| 執(zhí)行次數(shù) | 可能多次(需手動(dòng)控制) | 嚴(yán)格一次(自動(dòng)控制) |
| 適用邏輯 | 日志記錄、動(dòng)態(tài)參數(shù)處理 | 認(rèn)證、鑒權(quán)、Trace ID 設(shè)置 |
| 核心方法 | doFilter | doFilterInternal |
| 是否依賴請求屬性 | 無自動(dòng)跟蹤 | 通過請求屬性或 ThreadLocal 跟蹤 |
舉例說明
示例 1:日志記錄(GenericFilterBean)
假設(shè)需要在每次請求的每個(gè)階段(如轉(zhuǎn)發(fā)前、轉(zhuǎn)發(fā)后)記錄日志,此時(shí)使用 GenericFilterBean:
@Component
@Order(2) // 可以定執(zhí)行順序,數(shù)字越小優(yōu)先級(jí)越高
public class MultiLogFilter extends GenericFilterBean {
@Override
public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
System.out.println("Log: " + req.getRequestURI() + " - Stage 1");
chain.doFilter(req, res); // 繼續(xù)處理,可能觸發(fā)多次
System.out.println("Log: " + req.getRequestURI() + " - Stage 2");
}
}
結(jié)果:如果請求被轉(zhuǎn)發(fā)多次,日志會(huì)重復(fù)記錄。
示例 2:認(rèn)證過濾器(OncePerRequestFilter)
確保身份驗(yàn)證邏輯僅執(zhí)行一次:
@Component
@Order(2) // 可以定執(zhí)行順序,數(shù)字越小優(yōu)先級(jí)越高
public class AuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
// 檢查 Token,僅執(zhí)行一次
String token = req.getHeader("Authorization");
if (token == null) {
res.sendError(401, "Unauthorized");
return;
}
chain.doFilter(req, res);
}
}
結(jié)果:即使請求被轉(zhuǎn)發(fā),認(rèn)證邏輯僅執(zhí)行一次。
總結(jié)
- 選 GenericFilterBean:當(dāng)需要靈活控制執(zhí)行次數(shù)或允許重復(fù)操作時(shí)。
- 選 OncePerRequestFilter:當(dāng)需要嚴(yán)格保證邏輯僅執(zhí)行一次時(shí)。
- 關(guān)鍵方法:
doFilter(Generic) vsdoFilterInternal(OncePer)。 - 典型場景:日志(Generic) vs 認(rèn)證(OncePer)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Jmeter參數(shù)化獲取序列數(shù)據(jù)實(shí)現(xiàn)過程
這篇文章主要介紹了Jmeter參數(shù)化獲取序列數(shù)據(jù)實(shí)現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
Springboot中的@ConditionalOnBean注解使用指南與最佳實(shí)踐(工作原理)
在使用Spring Boot進(jìn)行開發(fā)時(shí),大家應(yīng)該都聽說過條件注解Conditional Annotations,下面給大家聊聊Springboot中的@ConditionalOnBean注解使用指南與最佳實(shí)踐,感興趣的朋友一起看看吧2025-05-05
Spring boot @RequestBody數(shù)據(jù)傳遞過程詳解
這篇文章主要介紹了Spring boot @RequestBody數(shù)據(jù)傳遞過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
基于MybatisPlus插件TenantLineInnerInterceptor實(shí)現(xiàn)多租戶功能
這篇文章主要介紹了基于MybatisPlus插件TenantLineInnerInterceptor實(shí)現(xiàn)多租戶功能,需要的朋友可以參考下2021-11-11
java.net.MalformedURLException異常的解決方法
下面小編就為大家?guī)硪黄猨ava.net.MalformedURLException異常的解決方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
Java實(shí)現(xiàn)post請求詳細(xì)代碼(帶有參數(shù))
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)帶有參數(shù)post請求的相關(guān)資料,文中通過代碼示例介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-08-08

