在 Spring Web 應用中,DelegatingFilterProxy 是連接 Servlet 容器 Filter 鏈與 Spring IOC 容器 Bean 的關鍵橋樑。本文深入源碼,揭示其初始化流程、委託機制、目標 Bean 查找策略、生命周期管理及常見配置陷阱。
javax.servlet.Filter (接口)
↑ 實現
org.springframework.web.filter.GenericFilterBean (抽象類)
↑ 繼承
org.springframework.web.filter.DelegatingFilterProxy (核心類)
GenericFilterBean 實現 Filter.init(FilterConfig),內部調用模板方法 initFilterBean(),留給子類實現具體初始化邏輯。DelegatingFilterProxy 重寫 initFilterBean(),完成「從 Web.xml filter-name 解析目標 Bean 名 → 獲取 WebApplicationContext → 從 IOC 容器獲取目標 Filter Bean → 初始化委託對象」全流程。@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// 1. 若未指定 targetBeanName,默認使用 filter-name
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// 2. 獲取 Spring 根上下文
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
// 3. 早期初始化委託對象(若上下文已就緒)
this.delegate = initDelegate(wac);
}
// 若上下文未就緒,延遲到首次 doFilter 時懶加載
}
}
}
WebApplicationContextUtils.getWebApplicationContext(servletContext)(根上下文,ContextLoaderListener 初始化)。contextAttribute 指定上下文屬性名。IllegalStateException 或延遲初始化。protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
// 1. 從 IOC 容器獲取目標 Filter Bean(必須實現 javax.servlet.Filter)
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
// 2. 若配置 targetFilterLifecycle=true,調用委託對象的 init(FilterConfig)
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
// 懶加載:首次請求時若 delegate 為 null,嘗試初始化
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
}
if (delegateToUse == null) {
throw new ServletException("Delegate Filter 未找到: " + getTargetBeanName());
}
// 真正執行業務過濾邏輯
delegateToUse.doFilter(request, response, chain);
}
<filter>
<filter-name>userInfoFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>customFilterBean</param-value> <!-- 可選:指定 Bean 名,默認同 filter-name -->
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value> <!-- 可選:是否調用目標 Filter 的 init/destroy,默認 false -->
</init-param>
</filter>
<filter-mapping>
<filter-name>userInfoFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<bean id="userInfoFilter" class="com.example.filter.UserInfoFilter">
<!-- 注入 Service、Repository 等 Spring Bean -->
<property name="userService" ref="userService"/>
</bean>
核心約定:
web.xml中的<filter-name>必須與 Spring 配置文件中目標 Filter Bean 的id/name完全一致(除非顯式指定targetBeanName)。
init(FilterConfig) → initFilterBean() → 查找/創建 delegate | 若 targetFilterLifecycle=true,調用 init(FilterConfig) |
| 請求處理 | doFilter → 懶加載 delegate → delegate.doFilter | 執行業務邏輯 |
| 容器關閉 | destroy() → 若 targetFilterLifecycle=true 調用 delegate.destroy() | 執行銷毀邏輯 |
filter-name ≠ Bean id → NoSuchBeanDefinitionException。統一命名規範或顯式配置 targetBeanName。getBean(..., Filter.class) 會拋 BeanNotOfRequiredTypeException。確保實現 javax.servlet.Filter。ContextLoaderListener 必須在 DelegatingFilterProxy 之前初始化(web.xml 順序)。targetFilterLifecycle=true 時,容器調用 DelegatingFilterProxy.init 會初始化 delegate,Spring 容器刷新時可能再次初始化同一 Bean。建議保持默認 false,由 Spring 管理完整生命周期。FilterRegistrationBean 註冊 Filter,直接注入 Spring Bean,更簡潔。DelegatingFilterProxy 通過模板方法模式在 GenericFilterBean.initFilterBean() 中完成「Servlet Filter 名 → Spring Bean 名 → IOC 容器查找 → 委託對象創建 → 生命周期綁定」的橋接。掌握其初始化時機、懶加載邏輯、targetFilterLifecycle 語義、Bean 名匹配規則,是排查 Spring Web 過濾器不生效、雙重初始化、上下文找不到等經典問題的關鍵。