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

