1. 概述

DelegatingFilterProxy 是 Spring 框架中一个非常关键的 Servlet Filter 代理类,它的核心作用是桥接 Servlet 容器与 Spring 应用上下文。通过它,我们可以让普通的 Servlet Filter 享受到 Spring 的依赖注入(DI)、生命周期管理等能力。

尤其是在 Spring Security 中,DelegatingFilterProxy 被广泛使用,比如著名的 springSecurityFilterChain 就是通过它注册进 Filter 链的。如果你在排查安全拦截失效问题时发现请求没进 Security,那大概率就是 DelegatingFilterProxy 配置出了问题。

本文将深入剖析它的机制,并演示如何结合 Java 和 XML 配置方式正确使用。


2. DelegatingFilterProxy 详解

DelegatingFilterProxy 的 Javadoc 中明确指出:

它是一个标准 Servlet Filter 的代理,实际工作委托给 Spring 容器中一个实现了 jakarta.servlet.Filter 接口的 Bean。

换句话说,它本身只是一个“中介”,真正的过滤逻辑由 Spring 管理的 Filter Bean 执行。

2.1 DelegatingFilterProxy 的工作原理

我们来拆解它的调用流程:

  1. 当 Web 容器启动时,会读取 web.xml 或 Java 配置中的 filter-class
  2. 如果这个类是 DelegatingFilterProxy,它就会根据 <filter-name> 去 Spring 的 ApplicationContext 中查找同名的 Bean。
  3. 查找到后,后续所有请求的 doFilter() 调用都会被转发给这个 Spring Bean。

✅ 核心优势:
这样一来,我们的 Filter 就不再是“孤立”的 Servlet 组件,而是可以:

  • 使用 @Autowired 注入其他 Spring Bean
  • 使用 @Value 读取配置
  • 实现 InitializingBeanDisposableBean 等生命周期接口

Java 配置示例

@Override
protected Filter[] getServletFilters() {
    DelegatingFilterProxy delegateFilterProxy = new DelegatingFilterProxy();
    delegateFilterProxy.setTargetBeanName("applicationFilter");
    return new Filter[]{delegateFilterProxy};
}

XML 配置示例

<filter>
    <filter-name>applicationFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

⚠️ 注意:filter-name 必须与 Spring 容器中目标 Filter Bean 的名称完全一致,否则启动时会抛出 NoSuchBeanDefinitionException


2.2 为什么需要 DelegatingFilterProxy?

直接写一个实现 Filter 的类不行吗?当然可以,但会有以下限制:

问题 使用 DelegatingFilterProxy 后
❌ 无法使用 @Autowired 注入服务 ✅ 可以正常使用 DI
❌ 配置项需手动读取 ✅ 可用 @Value@ConfigurationProperties
❌ 生命周期管理困难 ✅ 支持 @PostConstruct / @PreDestroy
❌ 难以复用 Spring AOP、事务等特性 ✅ 完全支持

📌 典型应用场景:

  • Spring Security:安全过滤器链需要访问 UserDetailsServiceAuthenticationManager 等 Spring Bean
  • 自定义日志/监控过滤器:需要记录日志到数据库或发送到 Kafka
  • 多租户上下文注入:在请求开始时设置租户 ID

此外,它还支持基于 URL 路径灵活配置多个 Filter,比如对 /api/**/admin/** 使用不同的过滤逻辑。


3. 创建并配置自定义 Filter

接下来我们动手实现一个简单的日志过滤器,并通过 DelegatingFilterProxy 接入 Spring。

3.1 自定义 Filter 类

@Component("loggingFilter")
public class CustomFilter implements Filter {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomFilter.class);

    @Override
    public void init(FilterConfig config) throws ServletException {
        // 初始化逻辑(可选)
    }

    @Override
    public void doFilter(
        ServletRequest request, 
        ServletResponse response, 
        FilterChain chain
    ) throws IOException, ServletException {
        
        HttpServletRequest req = (HttpServletRequest) request;
        LOGGER.info("Request Info : {} {}", req.getMethod(), req.getRequestURI());
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 清理资源(可选)
    }
}

⚠️ 关键点:

  • 必须实现 jakarta.servlet.Filter 接口
  • 必须用 @Component 注册为 Spring Bean
  • Bean 名称(loggingFilter)必须与配置中的 filter-name 一致

3.2 Java 配置方式

继承 AbstractAnnotationConfigDispatcherServletInitializer 并重写 getServletFilters()

public class ApplicationInitializer 
  extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected Filter[] getServletFilters() {
        DelegatingFilterProxy delegateFilterProxy = new DelegatingFilterProxy();
        delegateFilterProxy.setTargetBeanName("loggingFilter");
        return new Filter[]{delegateFilterProxy};
    }
}

✅ 启动后访问任意接口,日志中会输出类似:

INFO  c.e.f.CustomFilter - Request Info : GET /hello

3.3 web.xml 配置方式

适用于传统 WAR 包部署项目:

<filter>
    <filter-name>loggingFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>loggingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

📌 注意事项:

  • <filter-class> 写的是 DelegatingFilterProxy,不是你的 CustomFilter
  • <filter-name>@Component("loggingFilter") 名称必须一致
  • /* 表示拦截所有请求,也可细化为 /api/*

4. 总结

DelegatingFilterProxy 虽然代码量不多,但在 Spring Web 项目中扮演着“隐形枢纽”的角色。它的设计非常巧妙,简单粗暴地解决了 Servlet 标准与 Spring IOC 容器之间的隔离问题

✅ 重点回顾:

  • 它是 Servlet Filter 和 Spring Bean 之间的桥梁
  • 必须确保 filter-name 与 Spring Bean 名称一致
  • Spring Security 重度依赖它来加载安全过滤链
  • 推荐优先使用 Java 配置,更清晰可控

踩坑提醒:
如果发现自定义 Filter 没生效,第一步检查 ApplicationContext 中是否存在对应名称的 Bean;第二步确认 filter-name 拼写是否一致,大小写敏感!

示例代码已托管至 GitHub:https://github.com/techblog/spring-filter-demo


原始标题:Overview and Need for DelegatingFilterProxy in Spring