共计 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 启动类,临时理解到这里,上面再给出一张解决配置类的调用链,以供后续浏览参考。
本篇文章后续将从 ConfigurationClassPostProcessor
的postProcessBeanDefinitionRegistry()
办法开始,对由 @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
注册到了 DefaultListableBeanFactory
的beanDefinitionMap中,所以 注册表中的配置类 指的就是 Springboot 的启动类(前文已知 Springboot 的启动类就是一个配置类),而 派生进一步的 bean 定义 ,就是将Springboot 启动类上 @EnableAutoConfiguration
和@ComponentScan
等注解加载的配置类解析为 BeanDefinition
并注册到 DefaultListableBeanFactory
的beanDefinitionMap中。临时不分明在 Springboot 启动流程中,ConfigurationClassPostProcessor
的 postProcessBeanDefinitionRegistry()
办法正文中提到的配置类是否会有除了 Springboot 启动类之外的配置类,欢送留言探讨。
即当初晓得,ConfigurationClassPostProcessor
的 postProcessBeanDefinitionRegistry()
办法次要解决指标就是 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
,最初缓存在ConfigurationClassParser
的configurationClasses中; - 创立
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 启动类对应 的 BeanDefinition
为 AnnotatedGenericBeanDefinition
,而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
增加到 ConfigurationClassParser
的configurationClasses
中,那么最为要害的各种 starter 中的配置类对应的 ConfigurationClass
是在哪里增加的呢,回到 ConfigurationClassParser
的parse()
办法,上面再给出其源码,如下所示。
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.");
}
}
当初在 ConfigurationClassParser
的parse()
办法的 this.deferredImportSelectorHandler.process();
这一行代码打断点,程序运行到这里时,ConfigurationClassParser
的 configurationClasses 如下所示。
可见此时 configurationClasses 中没有 starter 中的配置类对应的 ConfigurationClass
,往下执行一行,此时ConfigurationClassParser
的configurationClasses如下所示。
可见此时 starter 中的配置类对应的 ConfigurationClass
曾经被加载,至此 ConfigurationClassParser
解析 Springboot 启动类剖析结束。
当初剖析 ConfigurationClassBeanDefinitionReader
解析所有 ConfigurationClass
,并基于ConfigurationClass
创立 BeanDefinition
并缓存到注册表的 beanDefinitionMap 中。首先是 ConfigurationClassBeanDefinitionReader
的loadBeanDefinitions()
办法,如下所示。
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
最初都会注册到注册表中,即缓存到 DefaultListableBeanFactory
的beanDefinitionMap中。至此,ConfigurationClassBeanDefinitionReader
解析所有 ConfigurationClass
的大抵流程也剖析结束。
总结
由 @Configuration
注解润饰的配置类联合 @Bean
注解能够实现向容器注册 bean 的性能,同时也能够借助 @ComponentScan
,@Import
等注解将其它配置类扫描到容器中。Springboot的启动类就是一个配置类,通过 ConfigurationClassPostProcessor
解决 Springboot 启动类,能够实现将自定义的 bean,自定义的配置类和各种starter 中的配置类扫描到容器中,以达到主动拆卸的成果。