
《Spring核心技术》@ComponentScan注解(下)
五、源码解析
源码时序图整清楚了,那就整源码解析呗!
@ComponentScans注解与@ComponentScan注解在Spring源码中的执行流程,结合源码执行的时序图,会理解的更加深刻。
(1)运行案例程序启动类
案例程序启动类源码详见:spring-annotation-chapter-02工程下的io.binghe.spring.annotation.chapter02.componentscan.ComponentScanTest,运行ComponentScanTest类的main()方法。
可以看到,在ComponentScanTest类的main()方法中调用了AnnotationConfigApplicationContext类的构造方法,并传入了ComponentScanConfig类的Class对象来创建IOC容器。接下来,会进入AnnotationConfigApplicationContext类的构造方法。
(2)解析AnnotationConfigApplicationContext类的AnnotationConfigApplicationContext(Class<?>... componentClasses)构造方法
源码详见:org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class<?>... componentClasses)。
可以看到,在上述构造方法中,调用了refresh()方法来刷新IOC容器。
(3)解析AbstractApplicationContext类的refresh()方法
源码详见:org.springframework.context.support.AbstractApplicationContext#refresh()。
refresh()方法是Spring中一个非常重要的方法,很多重要的功能和特性都是通过refresh()方法进行注入的。可以看到,在refresh()方法中,调用了invokeBeanFactoryPostProcessors()方法。
(4)解析AbstractApplicationContext类的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)方法
源码详见:org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)。
可以看到,在AbstractApplicationContext类的invokeBeanFactoryPostProcessors()方法中调用了PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors()方法。
(5)解析PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法
源码详见:org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)。
由于方法的源码比较长,这里,只关注当前最核心的逻辑,如下所示。
可以看到,在PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法中,BeanDefinitionRegistryPostProcessor的实现类在执行逻辑上会有先后顺序,并且最终都会调用invokeBeanDefinitionRegistryPostProcessors()方法。
(6)解析PostProcessorRegistrationDelegate类的invokeBeanDefinitionRegistryPostProcessors(Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup)方法
源码详见:org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors(Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup)。
可以看到,在invokeBeanDefinitionRegistryPostProcessors()方法中,会循环遍历postProcessors集合中的每个元素,调用postProcessBeanDefinitionRegistry()方法注册Bean的定义信息。
(7)解析ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法
源码详见:org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)。
可以看到,在postProcessBeanDefinitionRegistry()方法中,会调用processConfigBeanDefinitions()方法。
(8)解析ConfigurationClassPostProcessor类的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法
源码详见:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(BeanDefinitionRegistry registry)。
这里,重点关注方法中的如下逻辑。
可以看到,在processConfigBeanDefinitions()方法中,创建了一个ConfigurationClassParser类型的对象parser,并且调用了parser的parse()方法来解析类的配置信息。
(9)解析ConfigurationClassParser类的parse(SetconfigCandidates)方法
源码详见:org.springframework.context.annotation.ConfigurationClassParser#parse(SetconfigCandidates)。
可以看到,在ConfigurationClassParser类的parse(SetconfigCandidates)方法中,调用了类中的另一个parse()方法。
(10)解析ConfigurationClassParser类的parse(AnnotationMetadata metadata, String beanName)方法
源码详见:org.springframework.context.annotation.ConfigurationClassParser#parse(AnnotationMetadata metadata, String beanName)
可以看到,上述parse()方法的实现比较简单,直接调用了processConfigurationClass()方法。
(11)解析ConfigurationClassParser类的processConfigurationClass(ConfigurationClass configClass, Predicatefilter)方法
源码详见:org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass(ConfigurationClass configClass, Predicatefilter)。
可以看到,在processConfigurationClass()方法中,会通过do-while()循环获取配置类和其父类的注解信息,SourceClass类中会封装配置类上注解的详细信息。在在processConfigurationClass()方法中,调用了doProcessConfigurationClass()方法。
(12)解析ConfigurationClassParser类的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法
源码详见:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)。
这里,只关注与@ComponentScan注解相关的逻辑,可以看到,在上述处理@ComponentScan注解的逻辑中,通过componentScanParser的parse()方法对@ComponentScan注解进行解析。
(13)解析ComponentScanAnnotationParser类的parse(AnnotationAttributes componentScan, String declaringClass)方法
源码详见:org.springframework.context.annotation.ComponentScanAnnotationParser#parse(AnnotationAttributes componentScan, String declaringClass)。
看到这里,大家是不是有一种豁然开朗的感觉,原来@ComponentScan注解是在这里解析的!!!!
可以看到,在parse()方法中,会创建一个ClassPathBeanDefinitionScanner类型的扫描器scanner,将@ComponentScan注解上配置的信息都设置到扫描器scanner中,最后调用扫描器scanner的doScan()方法进行扫描。
(14)解析ClassPathBeanDefinitionScanner类的doScan(String... basePackages)方法
源码详见:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan(String... basePackages)。
在doScan()方法中,会遍历传入的每个包路径,调用findCandidateComponents()方法来扫描带有注解(例如@Component注解)的类。
(15)解析ClassPathScanningCandidateComponentProvider类的findCandidateComponents(String basePackage)方法
源码详见:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents(String basePackage)。
可以看到,Spring在扫描类时,会调用scanCandidateComponents()方法。
(16)解析ClassPathScanningCandidateComponentProvider类的scanCandidateComponents(String basePackage)方法
源码详见:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)。
可以看到,在scanCandidateComponents()方法中,会调用isCandidateComponent()方法来判断当前注解是不是要扫描的注解。
(17)解析ClassPathScanningCandidateComponentProvider类的isCandidateComponent(MetadataReader metadataReader)方法
源码详见:org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader metadataReader)。
可以看到,isCandidateComponent()方法的逻辑还是比较简单的,就是遍历匹配excludeFilters和includeFilters指定的规则。
优先匹配excludeFilters指定的规则,如果匹配excludeFilters指定的规则,直接返回false。接下来,匹配includeFilters指定的规则,匹配成功,则调用isConditionMatch()方法进行条件匹配。
其中,对于includeFilters而言,默认的过滤规则如下所示。
可以看到,对于includeFilters而言,默认的过滤规则会匹配@Component注解,JSR-250中的注解和JSR-330中的注解。
(18)回到ClassPathScanningCandidateComponentProvider类的scanCandidateComponents(String basePackage)方法
在ClassPathScanningCandidateComponentProvider类的scanCandidateComponents(String basePackage)方法中,会将标注了@Component注解,JSR-250中的注解和JSR-330中的注解的类信息封装成ScannedGenericBeanDefinition类对象,也就是类的Bean定义信息。
ScannedGenericBeanDefinition类的构造方法如下所示。
(19)回到ClassPathBeanDefinitionScanner类的doScan(String... basePackages)方法
在ClassPathBeanDefinitionScanner类的doScan(String... basePackages)方法中,会将扫描到的类的Bean定义信息注册到IOC容器中,如下代码片段所示。
可以看到,在doScan()方法中,会调用registerBeanDefinition()注册Bean定义信息。
(20)解析ClassPathBeanDefinitionScanner类的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法
源码详见:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)。
可以看到,在方法中直接调用了BeanDefinitionReaderUtils类的registerBeanDefinition()方法。
(21)解析BeanDefinitionReaderUtils类的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法。
源码详见:org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)。
可以看到,在上述代码中,会继续调用BeanDefinitionRegistry类型的对象registry的registerBeanDefinition()方法,将类的Bean定义信息注册到IOC容器。
(22)解析DefaultListableBeanFactory类的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法
源码详见:org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition(String beanName, BeanDefinition beanDefinition)。
可以看到,Spring会将扫描到的标注了符合过滤规则的注解的类封装成对应的Bean定义信息,最终会将这些Bean定义信息注册到beanDefinitionMap中。这一点和第1章中,注册ConfigurationClassPostProcessor类的Bean定义信息有点类似。
好了,至此,@ComponentScans注解与@ComponentScan注解在Spring源码中的执行流程分析完毕。
六、总结
@ComponentScans注解与@ComponentScan注解讲完了,一起来总结下吧!
本章,主要对@ComponentScans注解与@ComponentScan注解进行了系统性的介绍。
首先,对@ComponentScans注解与@ComponentScan注解的源码和使用场景进行了简单的介绍。
随后,给出了使用@ComponentScan注解自定义过滤规则的案例,并简单列举了其他应用案例。
接下来,重点分析了@ComponentScans注解与@ComponentScan注解的源码时序图和源码执行流程。
七、思考
既然学完了,就开始思考几个问题吧?
- Spring扫描指定包的逻辑看起来挺复杂的,Spring为何会这样设计?
- 如果使用Spring的注解开发应用程序,配置类上不标注@ComponentScans注解与@ComponentScan注解,能扫描到哪些包下的类?能将标注了@Component注解的类注入的IOC容器吗?
- @ComponentScan注解中的basePackages属性或者value属性可以设置任意包名吗(前提是包存在)?
本文转载自公众号:冰河技术
