美文网首页
深究SpringBoot是怎么做到自动装配的

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

作者: real阿苏勒 | 来源:发表于2020-07-04 02:22 被阅读0次

    深究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,构造器接收的参数是启动类的包名。

    相关文章

      网友评论

          本文标题:深究SpringBoot是怎么做到自动装配的

          本文链接:https://www.haomeiwen.com/subject/pyxyqktx.html