前两篇文章完成了SpringSecurity初始化过程的大部分逻辑分析。今天我们来看剩下的部分:FilterChainProxy是如何组装成DeletatingFilterProxy的?SecurityFilterAutoConfiguration我们先来看看SecurityFilterAutoConfiguration。这个配置类可以在spring.factories中看到调用入口。我们看他的securityFilterChainRegistration方法:@Bean@ConditionalOnBean(name=DEFAULT_FILTER_NAME)publicDelegatingFilterProxyRegistrationBeansecurityFilterChainRegistration(SecurityPropertiessecurityProperties){DelegatingFilterProxyRegistrationBeanregistration=newDEFTERFALTERBeanFilter(SecurityPropertiessecurityProperties);registration.setOrder(securityProperties.getFilter().getOrder());登记。setDispatcherTypes(getDispatcherTypes(securityProperties));返回注册;}此方法还将初始化SecurityPropertys。SecurityProperty我们暂时不去详细分析,等我们分析SpringSecurity的具体用法时可能会继续接触。然后,新建一个DelegatingFilterProxyRegistrationBean对象,传入的参数为DEFAULT_FILTER_NAME(=springSecurityFilterChain)。该对象的实例化方法为:publicDelegatingFilterProxyRegistrationBean(StringtargetBeanName,ServletRegistrationBean>...servletRegistrationBeans){super(servletRegistrationBeans);Assert.hasLength(targetBeanName,"TargetBeanName不能为null或为空");this.targetBeanName=targetBeanName;setName(targetBeanName);我们可以看到传入的参数“springSecurityFilterChain”赋值给了对象的targetBeanName属性。ServletContextInitializer我们首先来熟悉一下ServletContextInitializer的类结构:用于以编程方式配置Servlet3.0+上下文的接口。与WebApplicationInitializer不同,实现此接口(并且不实现WebApplicationInitializer)的类将不会被SpringServletContainerInitializer检测到,因此不会被Servlet容器自动引导。此接口旨在以与ServletContainerInitializer类似的方式运行,但具有由Spring而不是Servlet容器管理的生命周期。Servlet3.0+规范下配置servlet上下文,与WebApplicationInitializer不同,实现了这个接口的类(没有实现WebApplicationInitializer接口)不会被SpringServletContainerInitializer自动检测到,所以不会被Servlet自动初始化容器。此接口的设计行为类似于ServletContainerInitializer,但其生命周期由Spring而不是Servlet容器管理。大致意思就是在SpringMVC框架下,这个接口的实现类会被初始化为Servlet的上下文,实现Servlet相关的功能。好了,这里先大概了解一下,在后续的SpringMVC或者SpringBoot+Tomcat的分析中去摸索。我们现在需要知道,这个接口的实现类在Servlet容器启动的时候会被初始化,调用它的onStartup接口。@FunctionalInterfacepublicinterfaceServletContextInitializer{/***使用初始化所需的任何servlet、过滤器、侦听器*上下文参数和属性配置给定的{@linkServletContext}。*@paramservletContext{@codeServletContext}初始化*@throwsServletExcep如果对给定的{@codeServletContext}有任何调用*抛出{@codeServletException}*/voidonStartup(ServletContextservletContext)throwsServletException;}ServletWebServerApplicationContext#onRefresh()SpringBoot在Tomcat启动方法中最终会调用ServletWebServerApplicationContext。此方法将调用createWebServer()方法。然后调用getSelfInitializer()方法,再调用selfInitialize方法。我们可以看到在这个selfInitialize方法中调用了ServletContextInitializer的onStartup方法。privatevoidselfInitialize(ServletContextservletContext)throwsServletException{prepareWebApplicationContext(servletContext);registerApplicationScope(servletContext);WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),servletContext);对于(ServletContextInitializerbeans:getServletContextInitializerBeans()){beans.onStartup(servletContext);Beans中包含了很多servlet初始化对象,包括DelegatingFilterProxyRegistrationBean,我们简单跟踪一下。看一下getServletContextInitializerBeans方法:protectedCollectiongetServletContextInitializerBeans(){可以看到新建了一个ServletContextInitializerBeans对象并返回,继续跟踪。ServletContextInitializerBeans实例化方法将属性initializerTypes设置为ServletContextInitializer.class,然后调用addServletContextInitializerBeans方法。publicServletContextInitializerBeans(ListableBeanFactorybeanFactory,Class...initializerTypes){this.initializers=newLinkedMultiValueMap<>();this.initializerTypes=(initializerTypes.length!=0)?Arrays.asList(initializerTypes):Collections.singletonList(ServletContextInitializer.class);addServletContextInitializerBeans(beanFactory);addAdaptableBeans(beanFactory);ListsortedInitializers=this.initializers.values().stream().flatMap((value)->value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE)).collect(Collectors.toList());this.sortedList=Collections.unmodifiableList(sortedInitializers);日志映射(this.initializers);}addServletContextInitializerBeans方法:privatevoidaddServletContextInitializerBeans(ListableBeanFactorybeanFactory){for(ClassinitializerType:this.initializerTypes){for(EntryinitializerBean:getOrderedBeansOfType(beanFactory,initializerType)){addServletContextBeanitializerBean(initializer)),beanFactory);}}}该方法从beanFactory中获取initializerType(ServletContextInitializer.class)的bean。从文章中我们知道DelegatingFilterProxyRegistrationBean类属于ServletContextInitializer,所以bean中会包含DelegatingFilterProxyRegistrationBean。不跟踪,我们知道我们已经获取到DelegatingFilterProxyRegistrationBean对象,我们返回继续跟踪该对象的onStartup方法。RegistrationBean#onStartuponStartup方法在父类RegistrationBean中实现:@OverridepublicfinalvoidonStartup(ServletContextservletContext)throwsServletException{Stringdescription=getDescription();if(!isEnabled()){logger.info(StringUtils.capitalize(description)+"未注册(禁用)");返回;}注册(描述,servletContext);}首先看getDescription()方法,它定义在AbstractFilterRegistrationBean中。AbstractFilterRegistrationBean#getDescription@OverrideprotectedStringgetDescription(){Filterfilter=getFilter();Assert.notNull(filter,"过滤器不能为空");返回“过滤器”+getOrDeduceName(过滤器);}继续跟踪getFilter()方法,这个方法定义在DelegatingFilterProxyRegistrationBean类中。DelegatingFilterProxyRegistrationBean#getFilter该方法创建一个新的DelegatingFilterProxy对象,并在它出现时立即返回它。实例化参数传递给targetBeanName。通过第一部分对配置类SecurityFilterAutoConfiguration的分析,我们知道targetBeanName="springSecurityFilterChain",在实例化过程中赋值给DelegatingFilterProxy对象的targetBeanname属性。@OverridepublicDelegatingFilterProxygetFilter(){returnnewDelegatingFilterProxy(this.targetBeanName,getWebApplicationContext()){@OverrideprotectedvoidinitFilterBean()throwsServletException{//不要在init()上初始化过滤器bean}};之后,后续代码将DelegatingFilterProxy对象添加到servlet容器的filterchain中。请求提交后,通过DelegatingFilterProxy实现SpringSecurity的安全功能。DelegatingFilterProxy是如何添加到servlet容器的过滤器链中的具体代码就不详细分析了。接下来我们简单分析一下DelegatingFilterProxy是如何将请求委托给SpringSecurity的安全过滤器的。这个问题的答案应该在DelegatingFilterProxy的dofilter方法中。DelegatingFilterProxy#doFilter的代码并不长。我们之前分析过DelegatingFilterProxy的初始化代码。我们知道对象的delegate属性为null,所以代码会转到initDelegate方法。publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainfilterChain)throwsServletException,IOException{//如有必要,延迟初始化委托。FilterdelegateToUse=this.delegate;如果(delegateToUse==null){同步(this.delegateMonitor){delegateToUse=this.delegate;if(delegateToUse==null){WebApplicationContextwac=findWebApplicationContext();if(wac==null){thrownewIllegalStateException("未找到WebApplicationContext:"+"未注册ContextLoaderListener或DispatcherServlet?");}delegateToUse=initDelegate(wac);}this.delegate=delegateToUse;}}//让委托执行实际的doFilter操作tion.invokeDelegate(delegateToUse,request,response,filterChain);}initDelegate方法从wac(SpringIoc容器)中获取名称为targetBeanName,类型为Filter.class的bean,大家还有印象吗?前面我们分析了DelegatingFilterProxy的targetBeanName,就是“springSecurityFilterChain”。protectedFilterinitDelegate(WebApplicationContextwac)抛出ServletException{StringtargetBeanName=getTargetBeanName();Assert.state(targetBeanName!=null,"没有设置目标bean名称");过滤器委托=wac.getBean(targetBeanName,Filter.class);如果(isTargetFilterLifecycle()){delegate.init(getFilterConfig());}返回委托;}名为“springSecurityFilterChain”的bean在哪里神圣?如果忘记了,回去看我们之前的文章(SpringSecurity初始化过程(二)),就是FilterChainProxy。至此,下图中的所有主要逻辑,即SpringSecurity的filter初始化过程,已经从代码层分析完毕!!!收工!!!上一篇SpringSecurity初始化流程(二)下一篇SpringSecurity过滤器工作原理