Spring Web : DelegatingFilterProxy

这是一个过滤器对象,其实是一个代理。构造该过滤器对象时,通过名称或者对象方式指定一个Filter bean作为目标代理对象。最终对该过滤器对象的过滤操作的调用,都最终被委托给它所代理的Filter bean

比如使用Spring SecuritySpring Web应用中时,Sprng Security安全过滤器就是通过DelegatingFilterProxy来进行代理的。具体地讲,容器启动时,Sprng Security安全过滤器会作为一个名为springSecurityFilterChainFilter bean注入到容器中,实现类使用FilterChainProxy。应用启动时一个DelegatingFilterProxy Filter bean也会被注册到Servlet容器中,而该DelegatingFilterProxy Filter bean的目标代理对象就是名为springSecurityFilterChainFilter 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();
		}
	}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值