为什么启动类被【@SpringBootApplication】注解后,就会自动扫描其包内所有被【@Component】注解的类?
1、因为【@SpringBootApplication】又被【@ComponentScan】注解。
2、注解【@ComponentScan】有一个属性【useDefaultFilters】,并且默认值为【true】。
3、Spring初始化时会将启动类加入上下文的【BeanFactory】中,然后进行后置处理器的处理。
4、后置处理器中有一个默认的配置类后置处理器。
5、在配置类后置处理器中,有一个能否加入候选配置类集合的判断,判断依据为该类是否被【@Configuration】注解。
6、在判定为配置类并加入候选集合后,会挨个执行方法【parse】和【doProcessConfigurationClass】进行处理。此时会通过配置类的元数据中获取所有的【ComponentScan】注解,然后针对每个【ComponentScan】注解,通过扫描解析器【componentScanParser】进行扫描并获取所有符合条件的【BeanDefinitionHolder】。
7、在对每个【ComponentScan】注解执行扫描解析时,会先建立一个Bean定义扫描器【ClassPathBeanDefinitionScanner】。此时就会询问该【ComponentScan】注解的【useDefaultFilters】属性值了。
8、当【useDefaultFilters】的属性值为真时,该会对该扫描器注册默认的过滤器。
9、该注册过程的第一步就是对其【包含过滤器集合】---【includeFilters】增加对【@Component】注解的过滤。
10、在后续对每个【ClassPath】下找到的类文件进行过滤时,所有被【@Component】注解过的类就会成为候选项。所以虽然是自动完成的,但最终还是通过Spring的扫描过滤器实现的。
11、所有被扫描出的类定义中,如果还有被【@Configuration】注解过的配置类,则对其递归调用【parse】方法,继续重复这个过程。
配置类中被【@Bean】注解的【method】是通过后续的【retrieveBeanMethodMetadata】方法获取,并放入其【beanMethods】属性中。
12、通过遍历包路径下的类文件得到的配置类定义是如何加入到【BeanFactory】的呢?
我们在对被【@Configuration】注解的启动类作为候选项进行解析的时候,会先创建一个解析器,该解析器会包含对Spring上下文的信息,同时也包括实现了接口【BeanDefinitionRegistry】的【BeanFactory】的引用。
这样在扫描完成后,通过接口【BeanDefinitionRegistry】的方法【registerBeanDefinition】将获取到的候选【@Componet】Bean注册到Spring上下文的【BeanFactory】中。
13、配置类中被【@Bean】注解的方法是如何加入到【BeanFactory】中Bean定义集合的呢?
前面提到过在解析候选配置类时,除了将找到的被【@Component】注解的类定义加入【BeanFactory】中外,还通过方法【retrieveBeanMethodMetadata】,将配置类中被【@Bean】注解的方法收集到配置类【ConfigurationClass】的属性【beanMethods】中。
在解析完配置类自身后,又建立了一个Bean定义读取器【ConfigurationClassBeanDefinitionReader】来对解析后生成的配置类进行处理,从而获取其通过方法定义的Bean定义。
该Bean定义读取器和类文件解析器【ConfigurationClassParser】一样,建立时同样拥有对Spring上下文及实现了实现了【BeanDefinitionRegistry】接口的【BeanFactory】的引用。
在处理过程中通过配置类对象的【getBeanMethods】方法,获取被【@Bean】注解的方法,然后调用方法【loadBeanDefinitionsForBeanMethod】进行解析、构建和注册。
在将方法转换为Bean定义【BeanDefinition】后,然后执行接口【BeanDefinitionRegistry】的方法【registerBeanDefinition】,将其注入到【BeanFactory】的Bean定义集合中。
14、总体过程就是,Spring启动后先根据配置文件构建上下文运行环境,然后以启动类为起点扫描其包下的所有配置类,再扫描其它配置类,并形成【BeanDefinition】注册到【BeanFactory】中。然后遍历所有注册的配置类定义,将其在扫描过程中收集到的【@Bean】方法,转换为【BeanDefinition】并注册到【BeanFactory】。
后续应该就是对所有的【BeanDefinition】,根据其顺序、依赖关系、优先级等属性,进行实例化并注册到【BeanFactory】中,这部分的实现过程并没有再深入跟踪。