书接上文,上文书说到invokeBeanFactoryPostProcessors()方法,这个方法十分重要,也相当复杂,我们继续分析。
首先看下在执行完invokeBeanFactoryPostProcessors()方法之后,容器中发生了哪些变化:
上面框起来的部分就是执行这个方法之后容器中数据的变化,也就能初步知道这个方法做了什么了。这个方法主要是解析启动时构造方法传入的扫描类(在这个实例中是App.class),看这个类中是否存在@ComponentScan中,如果存在就会去扫描这个注解中配置的需要扫描的包路径下面的类,被扫描的包下面的类中包含@Component注解的类会将注入到容器中。也就发生了上面图中的变化。这个就是这个方法主要做的事情。
上面我们初步知道了这个方法所做的事情,那么我们通过代码来看下spring是如何处理的。
- 这个方法我们主要主要看第一行代码,PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors()方法,这个方法是核心处理逻辑,这个方法比较复杂,我们一点点来看下。
下面我们就来一点点分析PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors()方法。
- 下面方法是处理我们自定义的实现了BeanFactoryPostProcessor接口的实现类,执行其默认方法,这个我们先不用管,后续聊。
- 重点来了,下面的第一行方法是从容器中获取BeanDefinitionRegistryPostProcessor类型的处理器类的名字。我们看到了它获取到了什么?
获取到的如下beanName:
这个名字是不是有点熟悉呢?没错,它就是在我们之前说过的初始化AnnotatedBeanDefinitionReader实例的时候所注入的内置处理器ConfigurationClassPostProcessor。
beanFactory.getBeanNamesForType()方法具体处理逻辑我们在后续博文中会详细说明。
我们继续看本方法。获取到postProcessorNames[]之后,会调用getBean()方法取出其bean的实例,add到currentRegistryProcessors和processedBeans集合中。
- 接下来会执行下面的逻辑,前两行是对取出的处理器类进行排序,重点在第三行invokeBeanDefinitionRegistryPostProcessors():
其就是执行了每个处理器类的postProcessBeanDefinitionRegistry()方法,这里只处理ConfigurationClassPostProcessor类的这个方法。这个方法很复杂,我们看下具体实现:
首先会从容器中取出所有bean,然后取出类上存在@Configuration、@Component、@ComponentScan、@Import、@ImportResource注解的的bean,add到configCandidates临时变量中。
其中ConfigurationClassUtils.checkConfigurationClassCandidate()方法处理我们在之后博文中会详细解析。
- 下面方法是排序,不重要。
- 下图是核心的部分。那么我们就来看下parse()方法,ConfigurationClassParser.parse():
这个方法入参configCandidate其实就是App.class的包装类型BeanDefinitionHolder。随后调用其内部的parse方法:
也就是调用processConfigurationClass()方法,这个方法的参数类型是ConfigurationClass,这个类中封装了元数据信息,资源描述,bean名字。下面我们就看下processConfigurationClass()方法,这个方法中asSourceClass()和doProcessConfigurationClass()是比较重要的。
asSourceClass():
看上面的代码很清楚,这个方法就是获取App.class的注解并循环处理,我们App.class上的注解就一个@ComponentScan,然后会调用validateAnnotation()方法,这个方法会通过反射机制调用ComponentScan注解类里定义的方法,如下:具体验证了什么还不懂。
最后asSourceClass方法会将App.class封装成SourceClass对象返回。
- 接下来看doProcessConfigurationClass()方法做了什么,下面是这个方法中的核心部分之一,我们看这块处理了什么?这块会取出App.class中所有的@ComponentScan注解,然后通过this.componentScanParser.parse()方法处理当前@ComponentScan注解,componentScanParser就是ComponentScanAnnotationParser类的实例。
下面看下prase()方法,这个方法主要是处理App.class的@ComponentScan注解的方法配置值,有则取出,没有则设置默认值,包括includeFilters、excludeFilters、lazyInit、basePackage等,取出其中最重要的一行代码如下:
scanner是ClassPathBeanDefinitionScanner类型对象,我们看下这行代码做了什么?
主要是循环处理@ComponentScan里配置的包路径,也就是basePackages。其中上图红框中的两个方法是主要的处理方法:
findCandidateComponents()方法主要是取出当前包内所有包含@Component注解的类。
registerBeanDefinition()方法就是调用了DefaultListableBeanFactory类的registerBeanDefinition()方法,以下是这个方法的片段:
这个方法在第一节已经说明了,大家应该很熟悉了,就是将扫描出来的beanClass注入到bean工厂中(beanDefinitionMap和beanDefinitionNames)。以上就是本节开始说的容器中发生变化的处理逻辑,也就是为什么注解了@Component的类能注入到bean工厂的原因了。
上面我只说明了doProcessConfigurationClass()方法中的一个重要的处理逻辑(扫描需要扫描的包下所有需要注入到容器中的bean,完成注入),但是doProcessConfigurationClass()方法中还有一些重要的处理逻辑,比如对注解了@Import、@ImportSource等的beanClass是如何处理的,对beanClass中存在的内部类是如何处理的,等等这些内容是我们下一节重点说明的。想知道spring是如何处理这部分内容的,请关注我下一篇关于spring的博文。