chain.doFilter() 在 Spring Filter 中做什麼?
一、簡介
在本教程中,主要重點是了解 Spring 框架中chain.doFilter()
方法的用途。
為了更好地理解,我們首先探討什麼是過濾器、什麼是過濾器鏈以及使用過濾器的一些好的用例。然後我們將討論chain.doFilter()
方法的目的和重要性。此外,我們將了解如何在 Spring 中建立自訂過濾器。
最後,我們將探討與行為設計模式之一—責任鏈的相關性。
2. Spring中的Filter是什麼?
在 Spring 應用程式中,過濾器基於 Java Servlet 過濾器,它表示攔截請求和回應的物件。過濾器是 Java Servlet API 的一部分,在 Web 應用程式中發揮重要作用,因為它們位於客戶端和伺服器處理邏輯之間。
使用它們,我們可以在請求到達 servlet 之前或產生回應之後執行任務。過濾器的常見用例包括:
- 認證與授權
- 審計和日誌記錄
- 請求/回應修改
雖然過濾器不是 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. 責任鏈模式
現在我們了解了chain.doFilter()
能夠做什麼,讓我們簡要地看一下它與責任鏈模式的連結。我們不會詳細介紹,而是描述該模式是什麼以及它與我們的主題有何關係。
責任鏈模式是一種專注於物件之間互動的設計模式。它解釋瞭如何按順序組織處理程序(作為單獨的處理組件)來處理請求。每個處理程序執行特定任務,然後決定是否將請求傳遞給下一個處理程序以進行進一步處理。
如果我們將模式邏輯與 servlet 過濾器進行比較,我們會注意到過濾器是責任鏈模式的真實範例。每個過濾器充當一個單獨的處理程序,負責一部分處理邏輯。
使用此模式的好處包括靈活性,因為我們可以新增、刪除或重新排序過濾器,而無需修改其他元件。此外,責任鏈模式允許過濾器專注於單一任務,從而改善了關注點分離。
5. 實作自訂過濾器
實作自訂過濾器非常簡單,我們需要遵循幾個步驟。在我們的範例中,讓我們建立兩個帶有日誌語句的簡單過濾器,並展示chain.doFilter()
方法在實務上的用法。
5.1.建立Filter
首先,我們必須實作Filter
介面並重寫方法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()
實際操作
此時,當在我們的應用程式中執行任何請求時,我們可以看到回應沒有回傳。原因是我們省略了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 Filter
5.3.註冊Filter
值得注意的是,我們使用@Component
註解來註解過濾器以建立 Spring Bean。這一步很重要,因為它允許我們在 servlet 容器中註冊過濾器。
還有另一種註冊過濾器的方法,它提供了更多的控制和自訂,可以透過利用FilterRegistrationBean
類別來實現。使用該類,我們可以指定 URL 模式並定義過濾器的順序。
六、結論
在本文中,我們探討了過濾器、過濾器鏈以及chain.doFilter()
方法的正確使用。此外,我們還了解如何在 Spring 中建立和註冊我們自己的自訂過濾器。
此外,我們還討論了與責任鏈模式的聯繫,並強調了過濾器提供的靈活性和關注點分離。
與往常一樣,完整的程式碼範例可以在 GitHub 上找到。