SpringBoot自动配置原理解析(四)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: SpringBoot自动配置原理解析(四)

关联博文:

SpringBoot自动配置原理解析(一)

SpringBoot自动配置原理解析(二)

SpringBoot自动配置原理解析(三)

SpringBoot自动配置原理解析(四)


SpringBoot自动配置原理解析(五)

SpringBoot中如何将@Bean方法解析为BeanDefinition?


接上文SpringBoot自动配置原理解析(三)后,我们本文开始分析this.reader.loadBeanDefinitions(configClasses);。也就是ConfigurationClassBeanDefinitionReader的loadBeanDefinitions方法。

也就是在这个方法里面,对前面没有处理的@Bean注解的method、配置类引入的 比如AutoConfigurationPackages.Registrar以及扫描得到的XXXXAutoConfiguration做了处理。


【1】ConfigurationClassBeanDefinitionReader

如下所示,首先实例化一个TrackedConditionEvaluator ,然后对每个ConfigurationClass 执行loadBeanDefinitionsForConfigurationClass方法。

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

① loadBeanDefinitionsForConfigurationClass

loadBeanDefinitionsForConfigurationClass方法如下所示:

// ConfigurationClassBeanDefinitionReader
private void loadBeanDefinitionsForConfigurationClass(
    ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
//判断是否跳过当前configClass,如果跳过则从registry importRegistry移除数据
  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;
  }
// 如果当前类是通过@Import导入或者是某个配置类的内部类或者扫描得到的XXXAutoConfiguration
  if (configClass.isImported()) {
  // 注册configClass自身作为一个BeanDefinition放到beanDefinitionMap中
    registerBeanDefinitionForImportedConfigurationClass(configClass);
  }
  // 遍历配置类持有的BeanMethod,注册BeanDefinition到beanDefinitionMap中
  for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    loadBeanDefinitionsForBeanMethod(beanMethod);
  }
//获取配置类持有的importedResources,解析配置文件加载BeanDefinition
//如果你使用了@ImportResource(locations = {"classpath:beans.xml"}),这里就会解析beans.xml文件
  loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//获取配置类持有的importBeanDefinitionRegistrars,触发其registerBeanDefinitions方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}


梳理逻辑如下:


判断是否跳过当前configClass,如果跳过则从registry importRegistry移除数据

如果当前类是通过@Import导入或者是某个配置类的内部类或者是spring.factories扫描得到的xxxAutoConfiguration,则注册configClass自身作为一个BeanDefinition放到beanDefinitionMap中

遍历配置类持有的BeanMethod,注册BeanDefinition到beanDefinitionMap中

获取配置类持有的importedResources,解析配置文件加载BeanDefinition。如果你使用了@ImportResource(locations = {"classpath:beans.xml"}),这里就会解析beans.xml文件

获取配置类持有的importBeanDefinitionRegistrars,触发其registerBeanDefinitions方法

② loadBeanDefinitionsFromRegistrars

loadBeanDefinitionsFromRegistrars方法如下所示,对每一个Entry触发registrar的registerBeanDefinitions方法。

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
  registrars.forEach((registrar, metadata) ->
      registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}


我们以AutoConfigurationPackages.Registrar为例,这里会来到registerBeanDefinitions方法。


我们继续往下看AutoConfigurationPackages的register方法,本文这里只是实例化得到一个GenericBeanDefinition 并注册到registry中。

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
  if (registry.containsBeanDefinition(BEAN)) {
    BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
    ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
    constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
  }
  else {
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClass(BasePackages.class);
    beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(BEAN, beanDefinition);
  }
}


如下图所示,注册的beanName与beanDefinition。


也就是说这个方法以编程方式注册自动配置包名称。您可以使用此方法手动定义将用于给定BeanDefinitionRegistry的基本包。通常,建议不要直接调用此方法,而是依赖默认约定,其中包名是从@EnableAutoConfiguration配置类设置的。

【2】bean的实例化

如下所示,当我们do…while执行完毕后,我们有了442个BeanDefinition。


这些BeanDefinition在什么时候实例化呢?大部分在如下两个方法中:

//实例化BeanPostProcessor
registerBeanPostProcessors(beanFactory);
//实例化非懒加载的单例
finishBeanFactoryInitialization(beanFactory);


关于这两个方法可以参考博文SpringBoot启动流程分析之refresh方法详解

目录
相关文章
|
11天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
30 0
|
6天前
|
存储 前端开发 JavaScript
springboot中路径默认配置与重定向/转发所存在的域对象
Spring Boot 提供了简便的路径默认配置和强大的重定向/转发机制,通过合理使用这些功能,可以实现灵活的请求处理和数据传递。理解并掌握不同域对象的生命周期和使用场景,是构建高效、健壮 Web 应用的关键。通过上述详细介绍和示例,相信读者能够更好地应用这些知识,优化自己的 Spring Boot 应用。
15 3
|
14天前
|
Java 数据库连接
SpringBoot配置多数据源实战
第四届光学与机器视觉国际学术会议(ICOMV 2025) 2025 4th International Conference on Optics and Machine Vision
44 8
|
12天前
|
Java 数据库连接 数据库
springboot启动配置文件-bootstrap.yml常用基本配置
以上是一些常用的基本配置项,在实际应用中可能会根据需求有所变化。通过合理配置 `bootstrap.yml`文件,可以确保应用程序在启动阶段加载正确的配置,并顺利启动运行。
23 2
|
3天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
21 0
|
9天前
|
API 持续交付 网络架构
深入解析微服务架构:原理、优势与实践
深入解析微服务架构:原理、优势与实践
13 0
|
10天前
|
存储 供应链 物联网
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
|
10天前
|
存储 供应链 安全
深度解析区块链技术的核心原理与应用前景
深度解析区块链技术的核心原理与应用前景
19 0
|
Java Maven 容器
SpringBoot 核心原理分析
SpringBoot 核心原理分析
188 0
SpringBoot 核心原理分析
|
XML Java 数据格式
SpringBoot原理分析
SpringBoot原理分析
SpringBoot原理分析
下一篇
无影云桌面