盘点 SpringBoot : 自动装配

一 . 前言

无意中把 自动装配的相关源码补齐了, 所以终于能开启这个系列了.

这个系列我们来过一下自动装配的流程 , 整个流程从一年前就开始学习 , 一直没有找到合适的表达形式 , 最近灵感迸发 , 正好试试这个方式.

二 . 背景知识点

2.1 基础背景

Spring Boot 自动配置尝试根据添加的 jar 依赖项自动配置 Spring 应用程序 , Spring 是允许对自动装配进行替代 , 使Spring 的使用更灵活.

当然 , 配置是不可能平白无故产生 , 打开 Spring-boot-autoconfiture 包就可以看到对应的几个配置文件 :

  • spring.factories : 自动装配类 , Spring 启动的时候会用到 ,我们后面分析
  • spring-autoconfigure-metadata.properties : 配置信息
  • spring-configure-metadata.json : 配置元数据 , 业务中会通过该文件进行属性映射和处理
    • 例如 : spring.jpa.hibernate.ddl-auto 提供的多种模式

{
        "name": "spring.jpa.hibernate.ddl-auto",
        "values": [
            {
                "value": "none",
                "description": "Disable DDL handling."
            },
            {
                "value": "validate",
                "description": "Validate the schema, make no changes to the database."
            },
            {
                "value": "update",
                "description": "Update the schema if necessary."
            },
            {
                "value": "create",
                "description": "Create the schema and destroy previous data."
            },
            {
                "value": "create-drop",
                "description": "Create and then destroy the schema at the end of the session."
            }
        ]
    }

复制代码

自动装配和自动配置

  • 自动配置:是 Spring Boot 提供的,实现通过 jar 包的依赖,能够自动配置应用程序。
    • 例如说:我们引入 spring-boot-starter-web 之后,就自动引入了 Spring MVC 相关的 jar 包,从而自动配置 Spring MVC 。
  • 自动装配:是 Spring 提供的 IoC 注入方式,具体看看 《Spring 教程 —— Beans 自动装配》 文档。

2.2 基础使用

我们来看一下基础的使用 :

@Configuration
@EnableAutoConfiguration
public class BeanAnnotationConfig {

    @Bean
    public BeanAnnotationTestService getBeanAnnotationTestService() {
        return new BeanAnnotationTestService();
    }
}
复制代码

实现基础 : 自动装配基于 @SpringBootApplication 注解实现

F- SpringBootApplication
    |- @SpringBootConfiguration: 标记这是一个 Spring Boot 配置类
    |- @EnableAutoConfiguration : 用于开启自动配置功能
        |- @AutoConfigurationPackage : 获取主程序类所在的包路径,并将包路径(包括子包)下的所有组件注册到 Spring IOC 容器中
        |- @Import : 导入资源(并且由AutoConfigurationImportSelector)
    |- @ComponentScan(内部为excludeFilters )

复制代码

三. 源码分析

来尝试一种新的方式来解析源码 :

3.1 扫描的起点

3.2 业务扫描关系

我们先看下基本逻辑 :

1. 由 ConfigurationClassPostProcessor 开始
2. 在 ConfigurationClassPostProcessor 的 doProcessConfigurationClass 中获取所有的 config 类信息 , 其中就包括 @Bean 标注的Bean
3. 最终在 ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitionsForBeanMethod 中进行加载

C01- ConfigurationClassPostProcessor : 处理 processConfigBeanDefinitions 类型

M101M102M102 doWhile调用do-while parse 循环 处理ConfigurationClassParser 处理ConfigurationClassBeanDefinitionReader 处理判断已处理的和生成新得未处理candidates 为空 ,处理完成将ImportRegistry注册为一个bean清除外部提供的MetadataReaderFactory中的缓存M101M102M102 doWhile

C01- ConfigurationClassPostProcessor
    M101- postProcessBeanDefinitionRegistry
        - System.identityHashCode(registry) 获取一个 registryId 
        - 将 id 加入 Set<Integer>
        - processConfigBeanDefinitions(registry) : 详看 M102
    M102- processConfigBeanDefinitions
        - 从 BeanDefinitionRegistry 获取一个 List<BeanDefinitionHolder> 集合 
            ?- 通过每个 BeanDefinition 的 Attribute是否为 configurationClass , 加入集合
            ?- 此处是将 BeanDefinitionRegistry 获取的所有类过滤后加入
        - 通过 Order 进行一次排序
        - 获取相关 Generator ,并且通过这些 Generator 获取一个 ConfigurationClassParser 
        - List 封装为一个 Set<BeanDefinitionHolder> , 保证其唯一性
        FOR- 循环所有的 BeanDefinitionHolder
            - 通过 ConfigurationClassParser 处理该集合
            - 通过 ConfigurationClassBeanDefinitionReader load configClasses


// M01 伪代码
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    //.....
    this.registriesPostProcessed.add(registryId);
    processConfigBeanDefinitions(registry);
}

// M02 伪代码
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    
    // Step 1 : 获取 configurationClass 集合
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
    // .... 此处做了相关判断
    configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		
    // Step 2 : 排序
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // 省略 : 生成 ConfigurationClassParser 的必要参数
    
    // Step 3 : 准备一个 ConfigurationClassParser
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    // 准备所有的 ConfigClass 的 BeanDefinitionHolder 集合
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // Step 4 : parse 处理
        parser.parse(candidates);
        parser.validate();

        // 准备 ConfigurationClass 并且去掉已经处理的
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // Step 5 : reader 处理
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
        }
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

    }while (!candidates.isEmpty());
    
    // 将ImportRegistry注册为一个bean,以便支持importtaware @Configuration类
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // 清除外部提供的MetadataReaderFactory中的缓存
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}


复制代码

C02- ConfigurationClassParser : 主处理逻辑

ConfigurationClassParser 主要用于解析单个配置类 . 但是因为 @Import 注解的原因 , 可能会产生多个ConfigurationClass


M101M102M202M203M204调用FOR 循环处理 SourceClass调用 processConfigurationClass调用 doProcessConfigurationClass循环处理 PropertySources循环处理 ComponentScans递归处理 @Bean method递归处理 @Import获取超类返回超类或者null递归处理超类将处理类加入 Map 集合M101M102M202M203M204

C02- ConfigurationClassParser
    M201- parse(Set<BeanDefinitionHolder> configCandidates)
        - 循环 Set<BeanDefinitionHolder> , 根据其不同类型调用不同的parse
        - this.deferredImportSelectorHandler.process();
            ?- 调用内部类 DeferredImportSelectorHandler 处理相关的逻辑
    M202- processImports
        - this.importStack.push(configClass) : 
        // 从这里之后开始核心的 Class 类处理逻辑 , 会分别判断 SourceClass 的类型
        FOR- Collection<SourceClass> : for 循环传入的集合 (Collection - Group.Entry.getImportClassName) , 获取 SourceClass
            1- TODO
            2- TODO
            3- 除了以上2种外
                - processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter) : M203
    M203- processConfigurationClass
            - asSourceClass(configClass, filter) : 获取 SourceClass
            - sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter)  : M204
            - TODO 待完善
    M204- doProcessConfigurationClass
            - 如果类注解是 Component , 调用 processMemberClasses(configClass, sourceClass, filter)
            - For 循环 其上 PropertySources 注解 , 并调用 processPropertySource(propertySource)
            - For 循环 其上 ComponentScans 注解 
                - this.componentScanParser.parse 获取 Set<BeanDefinitionHolder>
                FOR- Set<BeanDefinitionHolder>
                    1- 获取 BeanDefinition
                    2- 如果检查给定的bean定义是配置类的候选 , 调用 parse(bdCand.getBeanClassName(), holder.getBeanName())
                        ?- 这里可以看成是一个递归处理
            - 调用 processImports 处理 @Import 注解对应的类
                ?-  这其实也是一个递归处理
            - 获取 ImportResource 对应的属性
            IF- 如果对应属性不为空
                - importResource.getStringArray("locations") : 获取其中的 locations 属性 String[]
                - 获取 BeanDefinitionReader
                FOR- 循环数组
                    - configClass.addImportedResource(resolvedResource, readerClass)
            -  retrieveBeanMethodMetadata(sourceClass) : 获取其中 @Bean 对应Method 
            FOR- 循环上面的 Set<MethodMetadata>
                - configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            - processInterfaces(configClass, sourceClass) : 处理其中 interface default 方法
            - 获取父类型  , 再将其添加到 Map<String, ConfigurationClass> 中
             - return sourceClass.getSuperClass() : 找到超类,返回其注释元数据并递归
    M05- processMemberClasses

复制代码

C03- DeferredImportSelectorHandler

C03- DeferredImportSelectorHandler
    M301- process() : 
       - 将 List<DeferredImportSelectorHolder> 排序后注入到 DeferredImportSelectorGroupingHandler 中 
       - 调用 handler.processGroupImports() 处理 所有的 Group
复制代码

C04- DeferredImportSelectorGroupingHandler

M301M401M501获取所有Group 后调用 processGroupImports 处理 Group获取 Group process 处理总逻辑 中 DeferredImportSelectorHolder执行 Group process 处理总逻辑M301M401M501

C04- DeferredImportSelectorGroupingHandler
    M401- processGroupImports
        - 先循环 DeferredImportSelectorGrouping
            - grouping.getImports() : 返回 Iterable<Group.Entry>
                - 获取 Group 中 DeferredImportSelectorHolder
                - DeferredImportSelector.Group 处理 (process) 每一个 DeferredImportSelectorHolder (C05 逻辑)
            FOR- 其次循环每个 DeferredImportSelectorGrouping 的 Imports
                 - this.configurationClasses.get(entry.getMetadata()) : 获取当前 ConfigurationClass
                 - processImports 处理 , 详见 processImports 方法逻辑 (M202)

复制代码

C05- AutoConfigurationImportSelector

从上一步中 , 其中一个 DeferredImportSelectorHolder 就是 AutoConfigurationImportSelector

M401M501M502M503调用通过 AnnotationMetadata 获取 AutoConfigurationEntry获取所有的 configurations 列表过滤重复及filter 过滤需要跳过的ConfigClass处理 listener 和 event返回一个 AutoConfigurationEntryAutoConfigurationEntry 加入 Map<String, AnnotationMetadata>M401M501M502M503


C05- AutoConfigurationImportSelector
    M502- getAutoConfigurationEntry
        - getAttributes(annotationMetadata) : 获取 AnnotationMetadata 中属性
        - getCandidateConfigurations(annotationMetadata, attributes) : 获取所有的 configurations 列表
        - removeDuplicates : 移除重复的配置类
        - checkExcludedClasses + removeAll : 移除  getExclusions(annotationMetadata, attributes) 获取的类
        - getConfigurationClassFilter().filter(configurations) 
            - filter.match(candidates, this.autoConfigurationMetadata) : filter 过滤是否跳过
        - fireAutoConfigurationImportEvents(configurations, exclusions) : 详见 fireAutoConfigurationImportEvents
        - new AutoConfigurationEntry(configurations, exclusions) : 返回一个 AutoConfigurationEntry
    M503- fireAutoConfigurationImportEvents
        - getAutoConfigurationImportListeners() : 获取AutoConfigurationImportListener集合
        - listener.onAutoConfigurationImportEvent(event) :分别通过每个 listern , 处理自动配置导入事件 


C05PSC01- AutoConfigurationGroup 
    ?- 该类为内部静态类
    M501- process : 
        - AutoConfigurationImportSelector.getAutoConfigurationEntry(annotationMetadata) : 获取一个 AutoConfigurationEntry 对象
            ?- 详见 getAutoConfigurationEntry 方法
        - this.autoConfigurationEntries.add(autoConfigurationEntry) : 添加到 AutoConfigurationEntry 集合中
        FOR- List<AutoConfigurationEntry> : For 循环整个 集合
            - this.entries.putIfAbsent(importClassName, annotationMetadata) : 放入  Map<String, AnnotationMetadata>
                ?- 这里意味着将所有的 AutoConfigurationEntry 重新处理成了 Map 

        
        
// M501 伪代码
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {                                      
    AutoConfigurationImportSelector.class.getSimpleName(),
    deferredImportSelector.getClass().getName()));
    // 调用 M502 生成 AutoConfigurationEntry
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    // 循环处理加入 放入  Map<String, AnnotationMetadata>
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

// M502 核心伪代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    
    // 移除和过滤
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    
    //  调用处理 Listener
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}



                
复制代码

C06- ConfigurationClass

从 C02-M204 中 ,我们可以看到 , 类上注解以及类中 Bean 都被配置到了 ConfigurationClass , 我们看看相关逻辑是什么样的

C06- ConfigurationClass
	-  getBeanMethods() : 观察后发现其被调用的地方是 ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

复制代码

C07- ConfigurationClassBeanDefinitionReader

C06M701M702M703加载 BeanDefinitions循环所有的的 @Bean 注解Method如果是 Imported , 调用 702 注册For 循环 loadBeanDefinitionsForBeanMethod判断@Bean @Lazy 等注解处理 autowire 等方法this.registry.registerBeanDefinition 注册C06M701M702M703

C07- ConfigurationClassBeanDefinitionReader
    M- loadBeanDefinitions
    M701- loadBeanDefinitionsForConfigurationClass
        - 判断当前 configClass 是否应该跳过
            true : 从相关对象中移除
                - this.registry.removeBeanDefinition(beanName)
                - this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName())
         - 如果是 Imported , 调用 registerBeanDefinitionForImportedConfigurationClass (M702)
        FOR- 循环处理 configClass.getBeanMethods() 获得的Bean 方法
            - loadBeanDefinitionsForBeanMethod(beanMethod) :M703
                
    M702- registerBeanDefinitionForImportedConfigurationClass
        - configClass.getMetadata() : 获取 AnnotationMetadata
        - 通过 AnnotationMetadata 生成 AnnotatedGenericBeanDefinition
        - 通过 AnnotatedGenericBeanDefinition 生成 ScopeMetadata , 并且为 AnnotatedGenericBeanDefinition 设置 Scope
        - 通过 BeanNameGenerator 生成一个 Config BeanName
        - 创建一个 BeanDefinitionHolder , 并且 this.registry.registerBeanDefinition 注册
        - configClass.setBeanName(configBeanName) : 为 Class 命名
    M703- loadBeanDefinitionsForBeanMethod(beanMethod) : 加载 Bean 主逻辑 , 此处已经到了方法处理
        - beanMethod.getConfigurationClass() : 获取当前的 ConfigurationClass
        - beanMethod.getMetadata() : 获取 MethodMetadata , 该元数据包含当前方法的信息
        - 如果当前方法应该跳过 , 则加入到 configClass.skippedBeanMethods , 并且返回
         - AnnotationConfigUtils.attributesFor(metadata, Bean.class) : 获取当前方法上 Bean 注解的属性
        - bean.getStringArray("name") 获取名称集合 , 取第一个为Bean 名 , 其他的作为别名
        - ????
        - 通过 configClass 和 metadata 构建一个 ConfigurationClassBeanDefinition
        - 为 ConfigurationClassBeanDefinition 设置 extractSource
                ?- configClass.getResource()
        IF- 判断是否为标注 @Bean 的 静态方法
                true:
                false: 设置 FactoryBeanName 和 UniqueFactoryMethodName
                    - beanDef.setFactoryBeanName(configClass.getBeanName()) : 设置了当前 config bean 的名称
                    - beanDef.setUniqueFactoryMethodName(methodName) : 当前 method 的名称
        - beanDef.setAutowireMode :设置注入的模式  , 此处是 AUTOWIRE_CONSTRUCTOR
            ?- AUTOWIRE_NO / AUTOWIRE_BY_NAME / AUTOWIRE_BY_TYPE / AUTOWIRE_CONSTRUCTOR / AUTOWIRE_AUTODETECT
        - beanDef.setAttribute : 设置 skipRequiredCheck 为 true
        - AnnotationConfigUtils.processCommonDefinitionAnnotations : 设置加载类型
             ?- 包括 Lazy , Primary , DependsOn , Role , Description 
        - bean.getEnum("autowire")  + beanDef.setAutowireMode(autowire.value())
        - bean.getBoolean("autowireCandidate") + setAutowireCandidate
        - bean.getString("initMethod") +  beanDef.setInitMethodName(initMethodName)
        - bean.getString("destroyMethod") + beanDef.setDestroyMethodName(destroyMethodName)
        - AnnotationConfigUtils.attributesFor(metadata, Scope.class) : 获取 + beanDef.setScope 配置 Scope 属性 
        - attributes.getEnum("proxyMode") : 获取 proxyMode
        - ScopedProxyCreator.createScopedProxy : 通过获取的 proxyMode +  registry + beanDef 等构建 BeanDefinitionHolder
        - 通过 BeanDefinitionHolder + configClass + metadata 构建 ConfigurationClassBeanDefinition
        - this.registry.registerBeanDefinition(beanName, beanDefToRegister) : 最后注册 Bean
                 ?- registry 是一个 BeanDefinitionRegistry

复制代码

总结

这是一篇测试型的文档 , 想测试一下该以什么样的形式来展现源码流程 , 也不知道这篇文档有没有达到效果.

后续还会对整篇文章继续处理 , 争取找到更合适的模式.

自动装配属于笔记里面比较早期的文章 , 本地还有很多 IOC , Cloud 的文章 , 找到合适的模式后应该能很好的展现了!!


作者:AntBlack
链接:https://juejin.cn/post/6949373202900451364
来源:掘金
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值