关于spring:SpringConfiguration注解简析

56次阅读

共计 18556 个字符,预计需要花费 47 分钟才能阅读完成。

前言

Spring中的 @Configuration 注解润饰的类被称为配置类,通过配置类能够向容器注册 bean 以及导入其它配置类,本篇文章将联合例子和源码对 @Configuration 注解原理进行学习,并引出对 Spring 框架在解决配置类过程中起重要作用的 ConfigurationClassPostProcessor 的探讨。

Springboot版本:2.4.1

注释

一. @Configuration 注解简析

基于 @Configuration 注解能够实现基于 JavaConfig 的形式来申明 Spring 中的 bean,与之作为比照的是基于XML 的形式来申明 bean。由@Configuration 注解标注的类中所有由 @Bean 注解润饰的办法返回的对象均会被注册为 Spring 容器中的bean,应用举例如下。

@Configuration
public class TestBeanConfig {

    @Bean
    public TestBean testBean() {return new TestBean();
    }

}

如上所示,Spring容器会将 TestBean 注册为 Spring 容器中的 bean。由@Configuration 注解润饰的类称为 Spring 中的配置类,Spring中的配置类在 Spring 启动阶段会被先加载并解析为 ConfigurationClass,而后会基于每个配置类对应的ConfigurationClass 对象为容器注册 BeanDefinition,以及基于每个配置类中由@Bean 注解润饰的办法为容器注册 BeanDefinition,后续Spring 也会基于这些 BeanDefinition 向容器注册 bean。对于BeanDefinition 的概念,能够参见 Spring-BeanDefinition 简析。

在详细分析由 @Configuration 注解润饰的配置类是如何被解析为 ConfigurationClass 以及最终如何被注册为 BeanDefinition 前,得先探索一下 Springboot 的启动类,因为后续的剖析会以 Springboot 的启动为根底,所以有必要先理解一下 Springboot 中的启动类。

Springboot的启动类由 @SpringBootApplication 注解润饰,@SpringBootApplication注解签名如下所示。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {......}

@SpringBootApplication注解的性能次要由 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan实现,后两者与 Springboot 中的主动拆卸无关,对于 Springboot 实现主动拆卸,会在后续文章中学习,在这里次要关怀 @SpringBootConfiguration 注解。实际上,@SpringBootConfiguration注解其实就是 @Configuration 注解,@SpringBootConfiguration注解的签名如下所示。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {......}

既然 @SpringBootConfiguration 注解等同于 @Configuration 注解,那么相应的 Springboot 的启动类就是一个配置类,Springboot的启动类对应的 BeanDefinition 会在筹备 Springboot 容器阶段就注册到容器中,将断点打到 SpringApplication#run() 办法中调用 refreshContext() 办法这一行代码,而已知 refreshContext() 这一行代码用于初始化容器,执行到 refreshContext() 办法时容器曾经实现了筹备,此时看一下容器的数据,如下所示。

此时 Springboot 容器持有的 DefaultListableBeanFactory 中的 beanDefinitionMap 中曾经存在了 Springboot 启动类对应的 BeanDefinition,在初始化Springboot 容器阶段,Springboot启动类对应的 BeanDefinition 会首先被解决,通过解决 Springboot 启动类对应的 BeanDefinition 才会引入对其它配置类的解决。对于 Springboot 启动类,临时理解到这里,上面再给出一张解决配置类的调用链,以供后续浏览参考。

本篇文章后续将从 ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry()办法开始,对由 @Configuration 注解润饰的配置类的解决进行阐明。

二. ConfigurationClassPostProcessor 解决配置类

通过第一节中的调用链可知,在 Springboot 启动时,初始化容器阶段会调用到 ConfigurationClassPostProcessor 来解决配置类,即由 @Configuration 注解润饰的类。ConfigurationClassPostProcessor是由 Spring 框架提供的 bean 工厂后置处理器,类图如下所示。

可知 ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,同时 BeanDefinitionRegistryPostProcessor 接口又继承于 BeanFactoryPostProcessor,所以ConfigurationClassPostProcessor 实质上就是一个 bean 工厂后置处理器。ConfigurationClassPostProcessor实现了 BeanDefinitionRegistryPostProcessor 接口定义的 postProcessBeanDefinitionRegistry() 办法,在 ConfigurationClassPostProcessor 中对该办法的正文如下。

Derive further bean definitions from the configuration classes in the registry.

直译过去就是:从注册表中的配置类派生进一步的 bean 定义。那么这里的 注册表 指的就是容器持有的 DefaultListableBeanFactory,而Springboot 框架在容器筹备阶段就将 Springboot 的启动类对应的 BeanDefinition 注册到了 DefaultListableBeanFactorybeanDefinitionMap中,所以 注册表中的配置类 指的就是 Springboot 的启动类(前文已知 Springboot 的启动类就是一个配置类),而 派生进一步的 bean 定义 ,就是将Springboot 启动类上 @EnableAutoConfiguration@ComponentScan等注解加载的配置类解析为 BeanDefinition 并注册到 DefaultListableBeanFactorybeanDefinitionMap中。临时不分明在 Springboot 启动流程中,ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry() 办法正文中提到的配置类是否会有除了 Springboot 启动类之外的配置类,欢送留言探讨。

即当初晓得,ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry() 办法次要解决指标就是 Springboot 的启动类,通过解决 Springboot 启动类引出对其它配置类的解决,上面追随源码,进行学习。postProcessBeanDefinitionRegistry()办法如下所示。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against" + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against" + registry);
    }
    // 记录曾经解决过的注册表 id
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

postProcessBeanDefinitionRegistry()办法会记录曾经解决过的注册表 id,避免同一注册表被反复解决。理论的解决逻辑在processConfigBeanDefinitions() 中,因为 processConfigBeanDefinitions() 办法比拟长,所以这里先把 processConfigBeanDefinitions() 办法的解决流程进行一个梳理,如下所示。

  • 先把 Springboot 启动类的 BeanDefinition 从注册表(这里指 DefaultListableBeanFactory,后续如果无非凡阐明,注册表默认指DefaultListableBeanFactory)的beanDefinitionMap 中获取进去;
  • 创立 ConfigurationClassParser,解析Springboot 启动类的 BeanDefinition,即解析@PropertySource@ComponentScan@Import@ImportResource@Bean 等注解并生成 ConfigurationClass,最初缓存在ConfigurationClassParserconfigurationClasses中;
  • 创立 ConfigurationClassBeanDefinitionReader,解析所有ConfigurationClass,基于ConfigurationClass 创立 BeanDefinition 并缓存到注册表的 beanDefinitionMap 中。

processConfigBeanDefinitions()办法源码如下。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    // 从注册表中把 Springboot 启动类对应的 BeanDefinition 获取进去
    for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class:" + beanDef);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // 如果未获取到 Springboot 启动类对应的 BeanDefinition,则间接返回
    if (configCandidates.isEmpty()) {return;}

    configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }

    if (this.environment == null) {this.environment = new StandardEnvironment();
    }

    // 创立 ConfigurationClassParser 以解析 Springboot 启动类及其引出的其它配置类
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
        //ConfigurationClassParser 开始执行解析
        parser.parse(candidates);
        parser.validate();

        // 将 ConfigurationClassParser 解析失去的 ConfigurationClass 拿进去
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // 创立 ConfigurationClassBeanDefinitionReader,以基于 ConfigurationClass 创立 BeanDefinition
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // 开始创立 BeanDefinition 并注册到注册表中
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

        candidates.clear();
        if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                            !alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());

    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}
}

processConfigBeanDefinitions() 办法中,ConfigurationClassPostProcessor将解析 Sprngboot 启动类以失去 ConfigurationClass 的工作委托给了 ConfigurationClassParser,将基于ConfigurationClass 创立 BeanDefinition 并注册到注册表的工作委托给了 ConfigurationClassBeanDefinitionReader,所以上面会对这两个步骤进行剖析。首先是ConfigurationClassParser 解析 Springboot 启动类,其 parse() 办法如下所示。

public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();
        try {if (bd instanceof AnnotatedBeanDefinition) {
                //Springboot 启动类对应的 BeanDefinition 为 AnnotatedGenericBeanDefinition
                //AnnotatedGenericBeanDefinition 实现了 AnnotatedBeanDefinition 接口
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {throw ex;}
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    
    // 提早解决 DeferredImportSelector
    this.deferredImportSelectorHandler.process();}

因为 Springboot 启动类对应 的 BeanDefinitionAnnotatedGenericBeanDefinition,而AnnotatedGenericBeanDefinition 实现了 AnnotatedBeanDefinition 接口,所以持续看 parse(AnnotationMetadata metadata, String beanName) 办法,如下所示。

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

持续看 processConfigurationClass() 办法,如下所示。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}

    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);
            }
            return;
        }
        else {this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
        // 理论开始解决配置类
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);
}

processConfigurationClass() 办法中会调用 doProcessConfigurationClass() 办法来理论的解决配置类的 @ComponentScan@Import@Bean 等注解。在本节的阐述中,其实始终是将 Springboot 启动类与其它配置类离开的,因为笔者认为 Springboot 启动类是一个非凡的配置类,其它配置类的扫描和加载均依赖 Springboot 启动类上的一系列注解(@ComponentScan@Import等)。上述 processConfigurationClass() 办法是一个会被递归调用的办法,第一次该办法被调用时,解决的配置类是 Springboot 的启动类,解决 Springboot 启动类时就会加载进来许多其它的配置类,那么这些配置类也会调用 processConfigurationClass() 办法来解决,因为其它配置类上可能也会有一些 @Import@Bean 等注解。这里只探讨第一次调用,即解决 Springboot 启动类的状况。doProcessConfigurationClass()办法源码如下所示。

@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
        throws IOException {if (configClass.getMetadata().isAnnotated(Component.class.getName())) {processMemberClasses(configClass, sourceClass, filter);
    }

    // 解决 @PropertySource 注解
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);
        }
        else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // 解决 @ComponentScan 注解
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // 解决 @Import 注解
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // 解决 @ImportResource 注解
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // 解决由 @Bean 注解润饰的办法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    processInterfaces(configClass, sourceClass);

    if (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);
            return sourceClass.getSuperClass();}
    }

    return null;
}

doProcessConfigurationClass()办法中对于每种注解的解决会在后续文章中介绍,本文临时不探讨。在 processConfigurationClass() 办法中解决完 Springboot 启动类之后,实际上此时只会将自定义 bean(由@Component@Controller@Service 等注解润饰的类)对应的 ConfigurationClass,自定义配置类(由@Configuration 注解润饰的类)对应的 ConfigurationClass 增加到 ConfigurationClassParserconfigurationClasses中,那么最为要害的各种 starter 中的配置类对应的 ConfigurationClass 是在哪里增加的呢,回到 ConfigurationClassParserparse()办法,上面再给出其源码,如下所示。

public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();
        try {if (bd instanceof AnnotatedBeanDefinition) {
                // 这里解决完,ConfigurationClassParser 的 configurationClasses 中只会有自定义 bean 和自定义配置类对应的 ConfigurationClass
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {throw ex;}
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }

    // 这里解决完,starter 中的配置类对应的 ConfigurationClass 才会增加到 ConfigurationClassParser 的 configurationClasses 中
    this.deferredImportSelectorHandler.process();}

因为 Springboot 扫描 starter 并解决其配置类是依赖启动类上的 @EnableAutoConfiguration 注解,@EnableAutoConfiguration注解的性能由 @Import(AutoConfigurationImportSelector.class) 实现,其中 AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口,而 DeferredImportSelector 表明须要被提早解决,所以 Springboot 须要提早解决 AutoConfigurationImportSelector,提早解决的中央就在上述parse() 办法的最初一行代码,对于 @Import 注解,后续文章中会对其进行剖析,这里临时不探讨。当初定义一个 TestBeanConfig 配置类,在其中向容器注册 TestBean,同时再定义一个由@Component 注解润饰的TestComponent,代码如下所示。

@Configuration
public class TestBeanConfig {

    @Bean
    public TestBean testBean() {return new TestBean();
    }

}
public class TestBean {public TestBean() {System.out.println("Initialize TestBean.");
    }

}
@Component
public class TestComponent {public TestComponent() {System.out.println("Initialize TestComponent.");
    }

}

当初在 ConfigurationClassParserparse()办法的 this.deferredImportSelectorHandler.process(); 这一行代码打断点,程序运行到这里时,ConfigurationClassParserconfigurationClasses 如下所示。

可见此时 configurationClasses 中没有 starter 中的配置类对应的 ConfigurationClass,往下执行一行,此时ConfigurationClassParserconfigurationClasses如下所示。

可见此时 starter 中的配置类对应的 ConfigurationClass 曾经被加载,至此 ConfigurationClassParser 解析 Springboot 启动类剖析结束。

当初剖析 ConfigurationClassBeanDefinitionReader 解析所有 ConfigurationClass,并基于ConfigurationClass 创立 BeanDefinition 并缓存到注册表的 beanDefinitionMap 中。首先是 ConfigurationClassBeanDefinitionReaderloadBeanDefinitions()办法,如下所示。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

loadBeanDefinitions() 办法中遍历每一个 ConfigurationClass 并调用了 loadBeanDefinitionsForConfigurationClass() 办法,持续看 loadBeanDefinitionsForConfigurationClass() 办法,如下所示。

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {if (trackedConditionEvaluator.shouldSkip(configClass)) {String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }

    if (configClass.isImported()) {
        // 基于 ConfigurationClass 本身创立 BeanDefinition 并缓存到注册表中
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        // 基于 ConfigurationClass 中由 @Bean 注解润饰的办法创立 BeanDefinition 并缓存到注册表中
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

上述 loadBeanDefinitionsForConfigurationClass() 办法中,除了将本身创立为 BeanDefinition 外,还会将所有由 @Bean 注解润饰的办法(如果有的话)创立为 BeanDefinition,所有创立的BeanDefinition 最初都会注册到注册表中,即缓存到 DefaultListableBeanFactorybeanDefinitionMap中。至此,ConfigurationClassBeanDefinitionReader解析所有 ConfigurationClass 的大抵流程也剖析结束。

总结

@Configuration 注解润饰的配置类联合 @Bean 注解能够实现向容器注册 bean 的性能,同时也能够借助 @ComponentScan@Import 等注解将其它配置类扫描到容器中。Springboot的启动类就是一个配置类,通过 ConfigurationClassPostProcessor 解决 Springboot 启动类,能够实现将自定义的 bean,自定义的配置类和各种starter 中的配置类扫描到容器中,以达到主动拆卸的成果。

正文完
 0