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 的工作原理
我们来拆解它的调用流程:
- 当 Web 容器启动时,会读取
web.xml
或 Java 配置中的filter-class
。 - 如果这个类是
DelegatingFilterProxy
,它就会根据<filter-name>
去 Spring 的ApplicationContext
中查找同名的 Bean。 - 查找到后,后续所有请求的
doFilter()
调用都会被转发给这个 Spring Bean。
✅ 核心优势:
这样一来,我们的 Filter 就不再是“孤立”的 Servlet 组件,而是可以:
- 使用
@Autowired
注入其他 Spring Bean - 使用
@Value
读取配置 - 实现
InitializingBean
、DisposableBean
等生命周期接口
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:安全过滤器链需要访问
UserDetailsService
、AuthenticationManager
等 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