深究SpringBoot是怎么做到自动装配的

2020-07-04  本文已影响0人  real阿苏勒

深究SpringBoot是怎么做到自动装配的

当配置了场景启动器,Springboot可以自动将配置的bean添加到iOC容器,它是怎么做到的。

自动装配的入口

在创建Springboot上下文时,会根据web应用类型,来创建上下文。
当前的上下文对象是AnnotationConfigServletWebServerApplicationContext。
在它的构造器方法中,会尝试注册注解配置相关的beanFactory后处理器。
也会添加两个过滤器,分别是AnnotationTypeFilter<Component>以及AnnotationTypeFilter<ManagedBean>。

其中包括有:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
=ConfigurationClassPostProcessor

org.springframework.context.annotation.internalAutowiredAnnotationProcessor
=AutowiredAnnotationBeanPostProcessor

org.springframework.context.annotation.internalCommonAnnotationProcessor
=CommonAnnotationBeanPostProcessor

org.springframework.context.annotation.internalPersistenceAnnotationProcessor
=PersistenceAnnotationBeanPostProcessor

org.springframework.context.event.internalEventListenerProcessor
=EventListenerMethodProcessor

org.springframework.context.event.internalEventListenerFactory
=DefaultEventListenerFactory

=前面是beanName,后面是beanClass。
在准备Springboot上下文阶段,会遍历所有的系统初始化器,调用其初始化方法。
在这个过程中,会创建三个beanfactory后处理器。分别是:

SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
可以发现它们都是内部类形式。

上面就是Springboot为我们配置的一些beanFactory后处理器,而自动装配的入口,正是在refresh流程的invokeBeanFactoryPostProcessors方法中。
入口是在ConfigurationClassPostProcessor的后处理阶段。

配置类解析

下面详细分析ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法处理流程。

(1)遍历所有注册beandefinition,通过判断找出配置类。

首先注册的beandefinition包含有Springboot启动类,在遍历期间,也会对它进行判断。
(Springboot启动类已经添加到beandefinition容器,创建的时机是在准备Springboot上下文阶段。)

具体的判断逻辑是:
对于BeanFactoryPostProcessor、BeanPostProcessor类型的beanClass,不会认为是配置类。
要知道是否是配置类,会获取对应的metadata,即元数据。(元数据包括有类的注解的信息)
1、元数据包含有@Configuration注解的类,被认为是配置类。
2、元数据包含有@Import、@Component、@ImportResource、@ComponentScan,或者该类中有带有@Bean注解的方法,也会被认为是配置类。

不管如何,此时只有springboot启动类满足条件是配置类。原因就是,此时的入口是beanFactory的后处理的处理阶段,此时被加载到bd容器中的,满足上述条件的是只有springboot启动类。(其他乱七八糟的bean后处理和beanfactory后处理器都不满足)

(2)解析配置类

构建ConfigurationClassParser去解析配置类的配置。
processConfigurationClass是配置类解析方法。

1、解析前
解析前,会判断配置类上是否有@Conditional注解,若有的话,会根据@Conditional注解的配置,决定是否应当跳过解析。(@Conditional会设置一些条件,所以这里会判断一下)

构建SourceClass,SourceClass封装了配置类Class和其元数据。在构建它时,会检查配置类的注解,会检查配置类的包名。(注意,配置类不要以“java.lang.annotation和"org.springframework.stereotype为包名)。

2、开始解析

2.1
判断当前类是否使用@Componet注解,若使用获取其内部类。再去判断其内部类是否是配置类。若是的话,就去解析该配置类。又会继续解析的过程。(解析的过程就是调用processConfigurationClass)

2.2、解析@PropertySources和@PropertySource
@PropertySource用于关联一个properties文件。会把注解属性封装为PropertySource,添加到当前环境的propertySources。

2.3、解析@ComponentScan和@ComponentScans
解析中,最关键的是获取@ComponentScan的注解属性basePackages的值。若basePackages未指定,会以配置类所在的包名为basePackages。对于Springboot启动类来说,就是它所在的包名。

其次是获取注解属性excludeFilters和includeFilters,它关联了一些filter,filter里面都有match方法,用于判断条件。excludeFilters指定的filter的matct若返回true,则表示不匹配,后者则相反。
在解析时,则会创建filter对象,分别添加到了includeFilters和excludeFilters。

excludefilter和includefilter:
springboot启动类包括有两个excludefilter,AutoConfigurationExcludeFilter和TypeExcludeFilter。
AutoConfigurationExcludeFilter,当某个类的包含有Configuration注解、并且该类的类名和默认自动装配的类名有相同的话,进行排除。(自动装配的类,是从类路径META-INF/spring.factories下获取org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类名)(前者是接口,后者是实现。)(所以你声明和自动装配类相同的全类名,且又是配置类的话,spring不会纳入iOC容器的管理)
includefilter,也有两个AnnotationTypeFilter。
AnnotationTypeFilter,类有@Component注解,match返回true。
AnnotationTypeFilter,类有@ManagedBean注解,match返回true。

接下来以basepackage为包名,构建包搜索路径。如果包名为com.asule.searchhouse,那么包搜索路径为:classpath:com/asule/searchhouse//.class。
那么会找到所有classpath下该包以及子包的class文件,并封装为Resource对象。

需要判断该class是否可以创建对象并添加到iOC容器。(不是basepackage下的所有类都能被创建bd,然后创建对象)。
判断的依据是根据@ComponentScan的excludeFilters和includeFilters。(includeFilters在创建Springboot上下文时,添加了两个。上面已经分析过了)

所以,基础包下的类若包含有@Component注解,@Configuration注解(内部包含@Component),会被构建为bd,并添加到bd容器中。(其中进行判断该bd是不是配置类,若是的话然后去解析)。

2.4、解析@Import
@Import引入的组件会加载到bean容器中。
搜寻类的@Import时,会从外到里的搜寻Import注解,对于Springboot启动类而言,它通过Import引入了两个组件。分别是AutoConfigurationImportSelector、AutoConfigurationPackages#Registrar。

搜寻到这两个组件,会去创建对象。
AutoConfigurationImportSelector是DeferredImportSelector类型。
Registrar是ImportBeanDefinitionRegistrar类型。

2.5、解析@ImportSource
ImportSource用于关联一个spring的配置文件。
反映在源码上是,该配置文件关联了一个bdreader,该reader用于读取配置文件并创建bd,添加到bd容器中。

2.4、解析@Bean
解析配置类下的是否有@Bean注解的方法,获得@Bean注解方法的元数据,封装为MethodMetadata。
再通过MethodMetadata构建BeanMethod,添加到对应配置类中。(配置类的beanMethods记录)

获取自动装配列表

解析完毕后,开始处理springboot启动类import引入的两个组件。
AutoConfigurationImportSelector理解为,有选择的自动配置引入器。
Registrar暂且理解为注册器。

下面来看看它们的工作流程:

ConfigurationClassParser.DeferredImportSelectorHolder它包装了AutoConfigurationImportSelector和configurationClass(即是Springboot启动类)。

AutoConfigurationImportSelector.AutoConfigurationGroup,把它理解为自动装配的组。
它注册时,把DeferredImportSelectorHolder到组中。(以后简称deferredImport)

获取自动装配的实体,调用AutoConfigurationImportSelector的getAutoConfigurationEntry。
(1)获取Springboot启动类@EnableAutoConfiguration注解的注解属性。
(2)获取默认支持的自动配置类列表,使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories,找出其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的接口实现,添加到configurations中。

(3)去除重复的配置类,若我们自己写的starter,则可能存在重复。

(4)如果项目中某些自动配置类,我们不希望其自动配置,我们可以通过EnableAutoConfiguration的exclude或excludeName属性进行配置。或者也可以在配置文件里通过配置项“spring.autoconfigure.exclude”进行配置。
这里会找出不希望自动配置的配置类。

(5)从默认支持的自动配置列表中,删除希望排除的配置类。

(6)加载spring-autoconfigure-metadata.properties文件,该文件内定义了AutoConfiguration配置类的条件值,比如@ConditionalOnClass、@ConditionalOnMissingClass等等值。
配置类只有满足这些默认的条件,才不会被过滤掉。
(该配置文件位于spring-boot-autoconfigure依赖中)


(7)发送自动配置导入事件,创建AutoConfigurationEntry对象返回。

//@ConditionalOnClass : 某个class位于类路径上,才会实例化这个Bean。
//@ConditionalOnMissingClass : classpath中不存在该类时起效
//@ConditionalOnBean : DI容器中存在该类型Bean时起效
//@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
//@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
//@ConditionalOnExpression : SpEL表达式结果为true时
//@ConditionalOnProperty : 参数设置或者值一致时起效
//@ConditionalOnResource : 指定的文件存在时起效
//@ConditionalOnJndi : 指定的JNDI存在时起效
//@ConditionalOnJava : 指定的Java版本存在时起效
//@ConditionalOnWebApplication : Web应用环境下起效
//@ConditionalOnNotWebApplication : 非Web应用环境下起效

配置类加载为beandefinition,注册到bd容器

要知道每当我们去解析配置类时,都会有一个配置类容器记录下我们的配置类。
其中自动装配类,也会去进行配置类解析,当然也会添加。
(是被configurationClasses记录。)

configurationClasses内记录着ConfigurationClass,每个ConfigurationClass内主要包含有该配置类的beanName和元数据。(自动装配的类都会被认为是配置类,因为包括有@Configuration注解)

遍历configurationClasses,importby为true的配置类,会被加载为beandefinition,并注册到bd容器中。(importby为true表示该类是被人引入的)
(自动装配的类是被springboot启动引入的,自动装配类的内部类是被自动装配类引入的)

会处理beanMethod,即该配置类中有@Bean注解相关的方法。处理也很简单,通过methodMetadata获取注解属性值,若没有声明beanName,就把methodName作为方法名。构建bd,添加到bd容器。

会处理ImportBeanDefinitionRegistrar,其中启动类中引入的Registrar就会被处理。会调用它的registerBeanDefinitions方法。该方法中注册了一个beandefinition,beanClass为AutoConfigurationPackages.BasePackages,构造器接收的参数是启动类的包名。

上一篇下一篇

猜你喜欢

热点阅读