名词约定:
配置类:指使用了@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean的类
前文讲到SpringBoot通过启动时传入的配置类,最终导入程序需要使用的所有配置类到spring中,以完成自动装配。本文主要讲述单个ConfigurationClass在它的一生中,是如何被处理的。
它的一生经历了两个阶段,一个是parse阶段,一个是read阶段。read阶段就是图的下半部分棕红色节点。
通过debug,一开始程序从spring容器里取出已经注册的BeanDefinition,然后通过ConfigurationClassUtils#checkConfigurationClassCandidate方法进行判断,过滤出能够成为配置类的BeanDefinition。然后将它们封装成ConfigurationClass。接下来,它们挨个经历parse和read阶段。
一、parse阶段由ConfigurationClassParser执行,它是对配置类进行解析,并设置ConfigurationClass的各属性。这里面有些方法值得多留意:
1. protected void processConfigurationClass(ConfigurationClass configClass),此方法是解析ConfigurationClass的主要入口,它除了主流程的调用外,它还会由于后续解析出多个配置类而被轮循和递归调用:
(1)当配置类被解析出有定义了内部类,就将该内部类封装成ConfigurationClass然后调用这方法;
(2)在解析ComponentScan标注后,对符合成为配置类的Bean封装成ConfigurationClass然后调用这方法;
(3)在解析Import标注环节,被引入的类并非ImportSelector、ImportBeanDefinitionRegistar和DeferredImportSelector接口的实现类时,将封装成ConfigurationClass然后调用这方法。
(4)在解析当前配置的父类时,稍稍有点特别,程序会跳转到processConfigurationClass方法里的稍后一点的地方,其实就是跳过是否是配置类的判断,然后这方法体的流程走下去。这么处理的逻辑可能是因为当前的类已经是配置类了,现在只是检查它父类的定义情况,所以就跳过了对父类是否配置类的判断了。
2. private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports),此方法是解析Import标注的入口。这方法也有被轮循递归调用的情况:
(1)当Import标注引入的类实现了ImportSelector接口,程序就调用该类的接口方法,得到需要引入的类名集合,然后封装成SourceClass集,接着递归调用processImports方法。
(2)当Import标注引入的类实现了ImportBeanDefinitionRegistar接口,程序会先收集到一个集合里,然后放到最后才挨个处理。处理的过程中,就会调用到processImports。如果该接口引入的类有可能是配置类,因此在processImports方法中,会将配置类封装成ConfigurationClass然后递归调用processConfigurationClass方法。如果该接口引入的类实现了ImportSelector、ImportBeanDefinitionRegistar和DeferredImportSelector接口,继续按processImports的处理规则分别处理。根据前文描述,如果实现的ImportSelector接口,程序就递归调用processImports方法,如果实现DeferredImportSelector接口,最后又会递归调用processImports方法。
总之,在debug时,一定要保持头脑清醒,别被绕晕了。
二、read阶段由ConfigurationClassBeanDefinitionReader执行,它将配置类的属性注册到spring去。本阶段就是轮循各个ConfigurationClass类,执行以下处理 :
(1)如果ConfigurationClass是被import的,注册到factory
(2)将beanMethods集合里的bean注册到factory
(3)将importedResources集合里的bean类注册到factory,即将配置文件里定义的bean类注册到factory
(4)将importBeanDefinitionRegistrars集合里的bean注册到factory
至此,单个ConfigurationClass的生命周就结束了。但它带来了更多的配置类,我觉得这就是自动装配的核心设计思想了。