这是一个过滤器对象,其实是一个代理。构造该过滤器对象时,通过名称或者对象方式指定一个Filter bean
作为目标代理对象。最终对该过滤器对象的过滤操作的调用,都最终被委托给它所代理的Filter bean
。
比如使用Spring Security
在Spring Web
应用中时,Sprng Security
安全过滤器就是通过DelegatingFilterProxy
来进行代理的。具体地讲,容器启动时,Sprng Security
安全过滤器会作为一个名为springSecurityFilterChain
的Filter bean
注入到容器中,实现类使用FilterChainProxy
。应用启动时一个DelegatingFilterProxy Filter bean
也会被注册到Servlet
容器中,而该DelegatingFilterProxy Filter bean
的目标代理对象就是名为springSecurityFilterChain
的Filter bean
。
源代码
源代码版本 : Spring Web 5.1.4.RELEASE
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class DelegatingFilterProxy extends GenericFilterBean {
@Nullable
private String contextAttribute;
@Nullable
private WebApplicationContext webApplicationContext;
// 目标代理对象Filter的 bean 名称
@Nullable
private String targetBeanName;
// 目标代理对象的生命周期和当前代理对象的生命周期要一致
private boolean targetFilterLifecycle = false;
// 目标代理对象 Filter
@Nullable
private volatile Filter delegate;
private final Object delegateMonitor = new Object();
/**
* Create a new DelegatingFilterProxy. For traditional (pre-Servlet 3.0) use
* in web.xml.
* @see #setTargetBeanName(String)
*/
public DelegatingFilterProxy() {
}
/**
* Create a new DelegatingFilterProxy with the given Filter delegate.
* Bypasses entirely the need for interacting with a Spring application context,
* specifying the #setTargetBeanName target bean name, etc.
* For use in Servlet 3.0+ environments where instance-based registration of
* filters is supported.
* @param delegate the Filter instance that this proxy will delegate to and
* manage the lifecycle for (must not be null).
* @see #doFilter(ServletRequest, ServletResponse, FilterChain)
* @see #invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain)
* @see #destroy()
* @see #setEnvironment(org.springframework.core.env.Environment)
*/
public DelegatingFilterProxy(Filter delegate) {
Assert.notNull(delegate, "Delegate Filter must not be null");
this.delegate = delegate;
}
/**
* Create a new DelegatingFilterProxy that will retrieve the named target
* bean from the Spring WebApplicationContext found in the ServletContext
* (either the 'root' application context or the context named by
* #setContextAttribute).
* For use in Servlet 3.0+ environments where instance-based registration of
* filters is supported.
* The target bean must implement the standard Servlet Filter.
* @param targetBeanName name of the target filter bean to look up in the Spring
* application context (must not be null).
* @see #findWebApplicationContext()
* @see #setEnvironment(org.springframework.core.env.Environment)
*/
public DelegatingFilterProxy(String targetBeanName) {
this(targetBeanName, null);
}
/**
* Create a new DelegatingFilterProxy that will retrieve the named target
* bean from the given Spring WebApplicationContext.
* For use in Servlet 3.0+ environments where instance-based registration of
* filters is supported.
* The target bean must implement the standard Servlet Filter interface.
* The given WebApplicationContext may or may not be refreshed when passed
* in. If it has not, and if the context implements ConfigurableApplicationContext,
* a ConfigurableApplicationContext#refresh() refresh() will be attempted before
* retrieving the named target bean.
* This proxy's Environment will be inherited from the given
* WebApplicationContext.
* @param targetBeanName name of the target filter bean in the Spring application
* context (must not be null).
* @param wac the application context from which the target filter will be retrieved;
* if null, an application context will be looked up from ServletContext
* as a fallback.
* @see #findWebApplicationContext()
* @see #setEnvironment(org.springframework.core.env.Environment)
*/
public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
this.setTargetBeanName(targetBeanName);
this.webApplicationContext = wac;
if (wac != null) {
this.setEnvironment(wac.getEnvironment());
}
}
/**
* Set the name of the ServletContext attribute which should be used to retrieve the
* WebApplicationContext from which to load the delegate Filter bean.
*/
public void setContextAttribute(@Nullable String contextAttribute) {
this.contextAttribute = contextAttribute;
}
/**
* Return the name of the ServletContext attribute which should be used to retrieve the
* WebApplicationContext from which to load the delegate Filter bean.
*/
@Nullable
public String getContextAttribute() {
return this.contextAttribute;
}
/**
* Set the name of the target bean in the Spring application context.
* The target bean must implement the standard Servlet Filter interface.
* By default, the filter-name as specified for the
* DelegatingFilterProxy in web.xml will be used.
*/
public void setTargetBeanName(@Nullable String targetBeanName) {
this.targetBeanName = targetBeanName;
}
/**
* Return the name of the target bean in the Spring application context.
*/
@Nullable
protected String getTargetBeanName() {
return this.targetBeanName;
}
/**
* Set whether to invoke the Filter.init and
* Filter.destroy lifecycle methods on the target bean.
* Default is "false"; target beans usually rely on the Spring application
* context for managing their lifecycle. Setting this flag to "true" means
* that the servlet container will control the lifecycle of the target
* Filter, with this proxy delegating the corresponding calls.
*/
public void setTargetFilterLifecycle(boolean targetFilterLifecycle) {
this.targetFilterLifecycle = targetFilterLifecycle;
}
/**
* Return whether to invoke the Filter.init and
* Filter.destroy lifecycle methods on the target bean.
*/
protected boolean isTargetFilterLifecycle() {
return this.targetFilterLifecycle;
}
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
// 获取目标代理对象 ,其实是真正要应用的Filter
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
// 针对当前请求执行目标代理Filter的过滤器逻辑
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
@Override
public void destroy() {
Filter delegateToUse = this.delegate;
if (delegateToUse != null) {
// 销毁目标代理对象
destroyDelegate(delegateToUse);
}
}
/**
* Return the WebApplicationContext passed in at construction time, if available.
* Otherwise, attempt to retrieve a WebApplicationContext from the
* ServletContext attribute with the #setContextAttribute
* configured name if set. Otherwise look up a WebApplicationContext under
* the well-known "root" application context attribute. The
* WebApplicationContext must have already been loaded and stored in the
* ServletContext before this filter gets initialized (or invoked).
* Subclasses may override this method to provide a different
* WebApplicationContext retrieval strategy.
* @return the WebApplicationContext for this proxy, or null if not found
* @see #DelegatingFilterProxy(String, WebApplicationContext)
* @see #getContextAttribute()
* @see WebApplicationContextUtils#getWebApplicationContext(javax.servlet.ServletContext)
* @see WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
*/
@Nullable
protected WebApplicationContext findWebApplicationContext() {
if (this.webApplicationContext != null) {
// The user has injected a context at construction time -> use it...
if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext cac = (ConfigurableApplicationContext)
this.webApplicationContext;
if (!cac.isActive()) {
// The context has not yet been refreshed -> do so before returning it...
cac.refresh();
}
}
return this.webApplicationContext;
}
String attrName = getContextAttribute();
if (attrName != null) {
return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
}
else {
return WebApplicationContextUtils.findWebApplicationContext(getServletContext());
}
}
/**
* Initialize the Filter delegate, defined as bean the given Spring
* application context.
* The default implementation fetches the bean from the application context
* and calls the standard Filter.init method on it, passing
* in the FilterConfig of this Filter proxy.
* @param wac the root application context
* @return the initialized delegate Filter
* @throws ServletException if thrown by the Filter
* @see #getTargetBeanName()
* @see #isTargetFilterLifecycle()
* @see #getFilterConfig()
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
// 从 wac Web应用上下文中尝试获取目标代理对象Filter
Filter delegate = wac.getBean(targetBeanName, Filter.class);
if (isTargetFilterLifecycle()) {
// 目标代理对象Filter的初始化动作
delegate.init(getFilterConfig());
}
return delegate;
}
/**
* Actually invoke the delegate Filter with the given request and response.
* @param delegate the delegate Filter
* @param request the current HTTP request
* @param response the current HTTP response
* @param filterChain the current FilterChain
* @throws ServletException if thrown by the Filter
* @throws IOException if thrown by the Filter
*/
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
/**
* Destroy the Filter delegate.
* Default implementation simply calls Filter.destroy on it.
* @param delegate the Filter delegate (never null)
* @see #isTargetFilterLifecycle()
* @see javax.servlet.Filter#destroy()
*/
protected void destroyDelegate(Filter delegate) {
if (isTargetFilterLifecycle()) {
// 目标代理对象的销毁动作
delegate.destroy();
}
}
}