SpringFramework之DelegatingFilterProxy簡析

注:分析的版本是SpringFramework-4.3.x,源碼可自行到Github上下載html

1.類繼承圖

先上一張圖java

                                            圖1 DelegatingFilterProxy的類繼承圖web

DelegatingFilterProxy只是直接繼承了GenericFilterBean,以下spring

    List-1app

public class DelegatingFilterProxy extends GenericFilterBean {

2.重要方法講解

DelegatingFilterProxy覆寫了父類中的以下幾個方法ide

    List-2this

@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?");
					}
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}

		// 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);
		}
	}

    使用Template pattern模式,DelegatingFilterProxy中的屬性targetBeanName,它是個bean的名稱,且這個bean要實現Filter。Spring會自動從web application中取出targetBeanName對應的bean,以後使用這個bean.url

    

    因爲DelegatingFilterProxy間接實現了Filter,因此要實現doFilter方法,以下:spa

    List-33d

@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?");
					}
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}

	/**
	 * 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);
	}

    在doFilter中調用了invokeDelegate方法,在invokeDelegate中,又調用了delegate的doFilter方法。

    調用的時序圖以下:

 

                                        圖2 DelegatingFilterProxy的doFilter調用時序圖

 

思考:

    1. DelegatingFilterProxy中有個屬性,以下List-4所示。爲何即便沒有顯示的指定webApplicationContext的值,DelegatingFilterProxy依然能夠從web container中獲得targetBeanName對應的bean,why? 這是由於DelegatingFilterProxy實現了Filter,而Filter接口中有個方法init,以下List-5所示,init方法的參數FilterConfig中有個方法getServletContext,getServletContext方法返回一個ServletContext,ServletContext有getAttribute方法,這個方法能夠拿到綁定到ServletContext的值。若是瞭解過ContextLoadListener和DispatcherServlet實現的,那麼應該明白了。

    List-4

private WebApplicationContext webApplicationContext;

    List-5

public void init(FilterConfig filterConfig) throws ServletException;

    List-6

public ServletContext getServletContext();

    2. 以下List-7所示,在web.xml中配置DelegatingFilterProxy時,咱們並無指定DelegatingFilterProxy的targetBeanName,那麼web container在處理DelegatingFilterProxy時,這個targetBeanName是什麼呢,它的值是不能爲null的。答案:在上面List-2中,方法initFilterBean()裏面,有this.targetBeanName = getFilterName(),那麼咱們來看看getFilterName()是如何實現的,以下List-8所示,因爲DelegatingFilterProxy間接實現了Filter,因此它能夠拿到FilterConfig,從FilterCofig中能夠拿到這個Filter的filterName,從List-8中看出,當targetBeanName爲null時,會用filterName進行賦值,按List-7中的配置的話,這個targetBeanName的值就是springSecurityFilterChain。注:問題2,我也是看了這篇博客後,才明白的。

    List-7 

<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>

    List-8

protected final String getFilterName() {
		return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
	}
相關文章
相關標籤/搜索