1. 簡介
本教程的主要目標是理解 Spring 框架中 chain.doFilter() 方法的用途。
為了獲得充分的理解,我們首先將探討什麼是過濾器、過濾器鏈以及利用過濾器的一些良好用例。然後,我們將討論 chain.doFilter() 方法的目的和重要性。此外,我們還將瞭解如何在 Spring 中創建自定義過濾器。
最後,我們將探討與行為設計模式之一——職責鏈模式的關聯。
2. Spring 中的過濾器是什麼?
在 Spring 應用中,過濾器基於 Java Servlet 過濾器,這些過濾器是攔截請求和響應的對象。 過濾器是 Java Servlet API 的一部分,在 Web 應用中起着重要作用,因為它們位於客户端和服務器端處理邏輯之間。
使用過濾器,我們可以在請求到達 Servlet 之前或響應生成之後執行任務。 過濾器的常見用例包括:
- 身份驗證和授權
- 審計和日誌記錄
- 請求/響應修改
雖然過濾器不是 Spring 框架的一部分,但它們與 Spring 框架完全兼容。 我們可以將它們註冊為 Spring Bean,並在我們的應用程序中使用它們。 Spring 提供了幾個過濾器的實現,其中一些常見實現包括 OncePerRequestFilter 和 CorsFilter。
3. 理解 chain.doFilter() 方法
在深入瞭解 chain.doFilter() 方法之前,首先需要理解過濾器鏈的概念及其在過濾過程中的作用。
過濾器鏈代表對傳入請求或傳出響應的預處理邏輯的順序執行流程。 換句話説,它是一組過濾器,用於對請求進行預處理或對響應進行後處理。 過濾器按照明確的順序排列,確保每個過濾器可以在將請求或響應傳遞到鏈中的下一階段之前執行其處理邏輯。
要定義一個過濾器鏈,我們使用 Java Servlet API 中的 FilterChain 接口,其中包含我們感興趣的方法。 如果我們檢查該方法的簽名,可以看到請求和響應對象被定義為輸入參數:
void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;過濾器鏈。doFilter() 方法將請求和響應傳遞給鏈中的下一個過濾器。如果鏈中沒有剩餘的過濾器,則請求將被轉發到目標資源(通常是 Servlet),並向客户端發送響應。
3.1. 調用 chain.doFilter() 的重要性是什麼?
如我們所見,doFilter() 方法在鏈中起着至關重要的作用,它確保請求會通過鏈中的所有過濾器。 如果省略該方法調用,請求將不會繼續到後續過濾器或servlet。 這可能導致應用程序行為異常,因為後續過濾器處理的任何邏輯都不會被執行。
另一方面,在身份驗證或授權失敗的情況下,我們可能希望跳過方法調用並中斷鏈。
4. 職責鏈模式
現在我們已經瞭解了 <em>chain.doFilter()</em> 能夠做什麼,接下來我們簡要地探討一下它與職責鏈模式之間的關係。 我們不會深入細節,而是會描述該模式是什麼以及它與我們討論的主題有什麼關聯。
職責鏈模式是一種 設計模式,專注於 對象之間的交互。 它解釋瞭如何組織處理組件(即處理者)成序列,以處理請求。 每個處理者執行特定的任務,然後決定是否將請求傳遞給下一個處理者進行進一步處理。
如果我們將模式邏輯與 Servlet 過濾器進行比較,我們會注意到過濾器是職責鏈模式的實際應用。 每個過濾器都充當一個獨立的處理者,負責處理邏輯的一部分。
使用該模式的優勢包括靈活性,因為我們可以添加、刪除或重新排序過濾器,而無需修改其他組件。 此外,職責鏈模式通過允許過濾器專注於單個任務,從而提高了分層設計。
5. 實現自定義過濾器
實現自定義過濾器相對簡單,我們需要遵循以下步驟。 在我們的示例中,我們創建一個兩個簡單的過濾器,並展示了 <em >chain.doFilter()</em> 方法在實踐中的用法。
5.1. 創建 過濾器</h3
首先,我們需要實現一個 過濾器 接口並覆蓋 doFilter() 方法。
需要注意的是,這個方法與過濾器鏈內的 doFilter() 方法並不相同。 過濾器的 doFilter() 方法作為入口點,用於實現過濾器的特定處理邏輯,而 chain.doFilter() 則用於將請求和響應傳遞給過濾器鏈中的下一個過濾器。
下面是如何實現過濾器並使用 @Order 註解來排列它們:
@Order(1)
@Component
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
LOG.info("Processing the First Filter");
// Omit chain.doFilter() on purpose
}
}
@Order(2)
@Component
public class SecondFilter implements Filter {
@Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOG.info("Processing the Second Filter");
chain.doFilter(request, response);
}
}5.2. 方法 chain.doFilter() 在 Action 中
此時,在執行我們應用程序中的任何請求時,我們發現響應並未返回。這是因為我們省略了 chain.doFilter() 調用,從而中斷了過濾器鏈。如果觀察控制枱,我們會注意到第二個過濾器的日誌缺失:
11:02:35.253 [main] INFO c.baeldung.chaindofilter.FirstFilter - Processing the First Filter為了恢復過濾鏈,我們應該在第一個過濾器中包含一個方法調用:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOG.info("Processing the First Filter");
chain.doFilter(request, response);
}在這種情況下,第一個過濾器成功地將請求和響應傳遞給第二個過濾器,過濾器鏈保持了連續的流程。
讓我們再次觀察控制枱,確認日誌語句存在。
11:02:59.330 [main] INFO c.baeldung.chaindofilter.FirstFilter - Processing the First Filter
11:02:59.330 [main] INFO c.baeldung.chaindofilter.SecondFilter - Processing the Second Filter5.3 註冊 過濾器
我們使用 @Component 註解對過濾器進行了標註,從而創建了一個 Spring Bean。這一步非常重要,因為它允許我們在 Servlet 容器中註冊該過濾器。
還有另一種註冊過濾器的方式,它提供了更多的控制和自定義選項,並且可以通過使用 FilterRegistrationBean 類來實現。通過該類,我們可以指定 URL 模式並定義過濾器的執行順序。
6. 結論
在本文中,我們探討了過濾器、過濾器鏈以及正確使用 chain.doFilter() 方法。我們還了解了如何在 Spring 中創建和註冊自定義過濾器。
此外,我們討論了與職責鏈模式的聯繫,並強調了過濾器提供的靈活性和關注點分離。