什么是 DelegatingFilterProxy
?
DelegatingFilterProxy
是 Spring 提供的一个特殊的过滤器,它起到了桥梁的作用,可以让你在 Spring 容器中管理 Servlet 容器中的过滤器。
为什么需要 DelegatingFilterProxy
?
通常情况下,Servlet 容器中的过滤器是由 Servlet 容器直接管理的,但这样有一些局限性,比如你不能方便地使用 Spring 的依赖注入来管理过滤器的依赖。通过使用 DelegatingFilterProxy
,你可以把过滤器放到 Spring 容器中管理,享受 Spring 提供的各种功能。
怎么理解 DelegatingFilterProxy
的工作流程?
- 配置过滤器 :你在
web.xml
文件中配置了一个过滤器,但这个过滤器实际上是DelegatingFilterProxy
。 - 委托处理 :
DelegatingFilterProxy
会将请求委托给 Spring 容器中定义的某个具体的过滤器(这个过滤器是一个 Spring Bean)。 - Spring 管理:这个具体的过滤器可以使用 Spring 的各种功能,比如依赖注入、事务管理等等。
示例:使用 DelegatingFilterProxy
配置 Spring Security
假设我们需要在一个 Spring 项目中使用 Spring Security 来管理权限验证。我们将通过 DelegatingFilterProxy
将 Spring Security 的过滤器配置到 Spring 容器中。
1. 配置 web.xml
在 web.xml
文件中,我们配置一个 DelegatingFilterProxy
,并指定 Spring Security 的过滤器 Bean 名称。
java
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 配置 DelegatingFilterProxy -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 其他配置 -->
</web-app>
2. 配置 Spring Bean
在 Spring 的配置文件中,我们定义 UserService
和 CustomSecurityFilter
Bean,并使用依赖注入。
applicationContext.xml
示例:
java
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 配置 Spring Security -->
<sec:http auto-config="true">
<sec:intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<sec:form-login login-page="/login" default-target-url="/home" />
<sec:logout logout-success-url="/login?logout" />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="user" password="password" authorities="ROLE_USER" />
<sec:user name="admin" password="password" authorities="ROLE_ADMIN" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
这样配置后,所有的 HTTP 请求都会经过 DelegatingFilterProxy
,它会将请求委托给 Spring 容器中的 Spring Security 过滤器链,完成权限验证和安全控制
(困惑)实际上 springSecurityFilterChain
是 Spring Security 内部自动配置的一个 Bean,具体如何配置和工作如下:
1. DelegatingFilterProxy
和 springSecurityFilterChain
在使用 DelegatingFilterProxy
时,它会在 Spring 容器中查找一个名为 springSecurityFilterChain
的 Bean。这个 Bean 是由 Spring Security 自动配置的,不需要你显式地在配置中定义。Spring Security 在启动时会自动创建这个 Bean,并将它注册为过滤器链。
所以整个请求流程
-
请求到达 Servlet 容器:
- 当客户端发起请求时,Servlet 容器开始处理这个请求。
-
查找匹配的过滤器:
- Servlet 容器根据
web.xml
中的<filter-mapping>
配置来决定哪些过滤器需要应用于这个请求。在你的配置中,所有的请求都匹配/*
,因此都会经过名为customSecurityFilter
的过滤器。
- Servlet 容器根据
java
<filter-mapping>
<filter-name>customSecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
调用 DelegatingFilterProxy
:
- Servlet 容器找到名为
customSecurityFilter
的过滤器,并创建DelegatingFilterProxy
的实例。此时,DelegatingFilterProxy
并不直接处理请求,而是作为一个代理来转发请求。
java
<filter>
<filter-name>customSecurityFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
-
委托给 Spring 容器中的 Bean:
DelegatingFilterProxy
使用filter-name
(即customSecurityFilter
)来查找 Spring 容器中的同名 Bean。在 Spring 容器中,customSecurityFilter
是一个实际的过滤器 Bean。DelegatingFilterProxy
会调用 Spring 容器中的customSecurityFilter
Bean 的doFilter
方法。这样,Spring 容器中的 Bean 就可以处理请求,并利用 Spring 的依赖注入等功能。
-
执行过滤器逻辑:
customSecurityFilter
Bean 处理请求。它可以依赖于其他 Spring 管理的 Bean,比如服务层的组件等。
图解流程
- 客户端请求 → 2. Servlet 容器 → 3. 查找
DelegatingFilterProxy
→ 4.DelegatingFilterProxy
查找 Spring 容器中的customSecurityFilter
Bean → 5.customSecurityFilter
Bean 执行过滤器逻辑 → 6. 继续处理请求或返回响应
java
客户端请求
↓
Servlet 容器
↓
DelegatingFilterProxy(代理)
↓
Spring 容器中的 customSecurityFilter Bean
↓
执行过滤器逻辑
↓
继续处理请求或返回响应
DelegatingFilterProxy
确实是通过 filter-name
来查找 Spring 容器中的同名 Bean
查找 Spring Bean:
在 DelegatingFilterProxy
的 init
方法中,它会根据 filter-name
找到 Spring 容器中的相应 Bean。这个过程包括:
- 获取过滤器名称 :通过
getFilterConfig().getFilterName()
获取配置的filter-name
。 - 从 Spring 容器中获取 Bean :使用
WebApplicationContextUtils
或类似的工具从 Spring 容器中查找与filter-name
匹配的 Bean。
java
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
String beanName = filterConfig.getFilterName();
ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
this.delegate = (Filter) applicationContext.getBean(beanName);
}
-
在上面的代码中,
beanName
是从filterConfig
获取的过滤器名称,然后从 Spring 容器中获取这个 Bean。 -
委托请求处理:
在
doFilter
方法中,DelegatingFilterProxy
将请求转发给它从 Spring 容器中获得的 Bean:
java
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (this.delegate == null) {
throw new ServletException("Delegate Filter not initialized");
}
this.delegate.doFilter(request, response, chain);
}
- 这里,
this.delegate
是从 Spring 容器中获取的实际过滤器 Bean,它的doFilter
方法被调用来处理请求。
总结
filter-name
:在web.xml
中配置的filter-name
用于标识DelegatingFilterProxy
要代理的 Spring Bean 名称。- Spring 容器 :
DelegatingFilterProxy
使用这个名称从 Spring 容器中查找实际的过滤器 Bean。 - 委托处理 :找到 Bean 后,
DelegatingFilterProxy
将请求委托给这个 Bean 来处理。
通过这些源码中的实现细节,可以确认 DelegatingFilterProxy
是如何使用 filter-name
查找和委托请求给 Spring 容器中的 Bean 的。
DelegatingFilterProxy
和普通的 Servlet 过滤器相比
普通的 Servlet 过滤器
在传统的 Servlet 过滤器中,你会直接实现 javax.servlet.Filter
接口,并在 doFilter
方法中处理请求。例如:
java
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化代码
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 过滤器逻辑
chain.doFilter(request, response); // 继续传递请求
}
@Override
public void destroy() {
// 清理资源
}
}
在 web.xml
中,你会配置这个过滤器,如下:
java
<filter>
<filter-name>customFilter</filter-name>
<filter-class>com.example.CustomFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>customFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
DelegatingFilterProxy
DelegatingFilterProxy
是 Spring 提供的一个特殊过滤器,它的工作原理略有不同。它的主要目的是将请求转发给 Spring 容器中的实际过滤器 Bean,而不是直接处理请求。DelegatingFilterProxy
实现了 javax.servlet.Filter
接口,但它的 doFilter
方法并不会直接处理请求,而是将请求委托给 Spring 管理的 Bean。
DelegatingFilterProxy
的工作流程:
-
初始化:
- 在
init
方法中,DelegatingFilterProxy
根据filter-name
从 Spring 容器中查找一个 Bean。这个 Bean 实现了javax.servlet.Filter
接口,并且是由 Spring 容器管理的。
- 在
java
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
String beanName = filterConfig.getFilterName();
ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
this.delegate = (Filter) applicationContext.getBean(beanName);
}
2.请求处理:
在 doFilter
方法中,DelegatingFilterProxy
将请求转发给 Spring 容器中的实际 Bean,而不是直接处理请求。
java
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (this.delegate == null) {
throw new ServletException("Delegate Filter not initialized");
}
this.delegate.doFilter(request, response, chain);
}
这里,this.delegate
是从 Spring 容器中获取的实际过滤器 Bean,它会处理请求。
-
清理资源:
- 在
destroy
方法中,DelegatingFilterProxy
不会执行任何操作,因为它不直接持有资源。
- 在
-
普通 Servlet 过滤器 :直接实现
Filter
接口,处理请求的逻辑在doFilter
方法中实现。 -
依赖注入:可以使用 Spring 的依赖注入功能来管理过滤器。
-
Spring 管理:将过滤器的配置和管理转移到 Spring 容器中,享受 Spring 提供的其他功能(如事务管理、AOP)。
-
这里,
this.delegate
是从 Spring 容器中获取的实际过滤器 Bean,它会处理请求。 -
清理资源:
- 在
destroy
方法中,DelegatingFilterProxy
不会执行任何操作,因为它不直接持有资源。
- 在
-
普通 Servlet 过滤器 :直接实现
Filter
接口,处理请求的逻辑在doFilter
方法中实现。 -
DelegatingFilterProxy
:作为一个代理,负责将请求委托给 Spring 容器中的实际过滤器 Bean,利用 Spring 的依赖注入等功能。 -
依赖注入:可以使用 Spring 的依赖注入功能来管理过滤器。
-
Spring 管理:将过滤器的配置和管理转移到 Spring 容器中,享受 Spring 提供的其他功能(如事务管理、AOP)。