专栏首页软件开发-青出于蓝SpringFramework之DelegatingFilterProxy简析

SpringFramework之DelegatingFilterProxy简析

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

1.类继承图

先上一张图

                                            图1 DelegatingFilterProxy的类继承图

DelegatingFilterProxy只是直接继承了GenericFilterBean,如下

List-1

public class DelegatingFilterProxy extends GenericFilterBean {

2.重要方法讲解

DelegatingFilterProxy覆写了父类中的如下几个方法

List-2

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

    由于DelegatingFilterProxy间接实现了Filter,所以要实现doFilter方法,如下:

List-3

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

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Springcloud之zuul的ZuulController

        Springcloud的版本是Greenwich.SR2,Springboot版本是2.1.6.release.

    克虏伯
  • 用MockMvc对Spring mvc中的controller层进行单元测试

    参考:http://sishuok.com/forum/posts/list/7981.html  ;  http://www.tuicool.com/arti...

    克虏伯
  • Spark之伪分布式搭建、伪分布式Hadoop、Hive安装

        之后进入$SPARK_HOME/sbin下,执行start-all.sh,可以看下是否启动成功,之后去看localhost:8080,可以看到spark...

    克虏伯
  • debounceFnHook vue函数防抖hook

    copy_left
  • 私人订制Android本地图片选择器

    史上最强的安卓图片选择器——GalleryFinal; 简书博客地址——http://www.jianshu.com/p/48ddd6756b7a

    代码咖啡
  • Caffe源码---------主要框架介绍

    初学者的我感觉看代码就是一个煎熬啊!但是某人说过一句话:“Don’t be afraid to read the code!”现在我写一下简单的介绍,准备给入门...

    计算机视觉研究院
  • 最大黑洞合并事件被发现,新黑洞约为太阳80倍大

    据报道,一个国际科学家团队最近通过分析高新激光干涉仪引力波天文台(Advanced LIGO)获得的观测数据,发现了迄今最大的黑洞合并事件和另外三起黑洞合并事件...

    镁客网
  • Newbe.Claptrap-一套以“事件溯源”和“Actor模式”作为基本理论的服务端开发框架

    本文是关于 Newbe.Claptrap 项目主体内容的介绍,读者可以通过这篇文章,大体了解项目内容。

    newbe36524
  • Django小技巧01: redirect

    redirect函数会返回一个HttpResponseRedirect类,比起HttpResponseRedirect类我更喜欢使用更简洁的redirect. ...

    用户1416054
  • Linux Vim中自动补全Python

    Pydiction 可以是我们使用Tab键自动补全Python代码在Vim,是一款非常不错的插件。

    py3study

扫码关注云+社区

领取腾讯云代金券