Spring Boot 属性加载原理解析

这篇具有很好参考价值的文章主要介绍了Spring Boot 属性加载原理解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基于Spring Boot 3.1.0 系列文章

  1. Spring Boot 源码阅读初始化环境搭建
  2. Spring Boot 框架整体启动流程详解
  3. Spring Boot 系统初始化器详解
  4. Spring Boot 监听器详解
  5. Spring Boot banner详解
  6. Spring Boot 属性配置解析
  7. Spring Boot 属性加载原理解析

在《Spring Boot 框架整体启动流程详解》中,我们了解到有一步是准备环境prepareEnvironment,属性加载就是在这一步开始的。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	//创建并配置环境
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	//配置环境,如果需要转换服务,添加ApplicationConversionService,另外委托给了configurePropertySources(属性源)和configureProfiles(配置文件),子类可以覆盖该方法或分别覆盖两者进行细粒度控制
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	//将ConfigurationPropertySource支持附加到指定的环境
	ConfigurationPropertySources.attach(environment);
	//调用environmentPrepared方法
	listeners.environmentPrepared(bootstrapContext, environment);
	//将defaultProperties属性源移动到指定配置环境的最后
	DefaultPropertiesPropertySource.moveToEnd(environment);
	Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
			"Environment prefix cannot be set via properties.");
			//绑定环境到SpringApplication
	bindToSpringApplication(environment);
	//非自定义环境配置,就将其转换为标准类型
	if (!this.isCustomEnvironment) {
		EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
		environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	//重新将ConfigurationPropertySource支持附加到指定的环境
	ConfigurationPropertySources.attach(environment);
	return environment;
}

进入getOrCreateEnvironment()

private ConfigurableEnvironment getOrCreateEnvironment() {
//判断environment 是否为null,不为null使用environment
	if (this.environment != null) {
		return this.environment;
	}
	//根据web应用程序类型,通过applicationContextFactory创建environment
	ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType);
	//如果environment为null,并且applicationContextFactory不是用的默认ApplicationContextFactory
	if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
	//使用默认的ApplicationContextFactory创建environment
		environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType);
	}
	//如果不为null返回environment,否则只显示创建一个ApplicationEnvironment
	return (environment != null) ? environment : new ApplicationEnvironment();
}

this.applicationContextFactory 由于没有显示设置,使用的是默认的ApplicationContextFactory
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();

进入createEnvironment(this.webApplicationType)中:

public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
	return getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, null);
}

进入getFromSpringFactories中:

private <T> T getFromSpringFactories(WebApplicationType webApplicationType,
		BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
		//循环获取ApplicationContextFactory类型的实例
	for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class,
			getClass().getClassLoader())) {
			//调用实例的createEnvironment方法
		T result = action.apply(candidate, webApplicationType);
		if (result != null) {
			return result;
		}
	}
	return (defaultResult != null) ? defaultResult.get() : null;
}

SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, getClass().getClassLoader())META-INF/spring.factories中获取并实例化ApplicationContextFactory实例,Spring Boot定义了ReactiveWebServerApplicationContextFactoryServletWebServerApplicationContextFactory,所以在这里会分别去调用其中的createEnvironment方法,由于这边是web环境,进入ServletWebServerApplicationContextFactory的createEnvironment中。

public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
//不是Web Servlet环境的话返回null,是的话创建一个ApplicationServletEnvironment
	return (webApplicationType != WebApplicationType.SERVLET) ? null : new ApplicationServletEnvironment();
}

进入ApplicationServletEnvironment类中,其继承了StandardServletEnvironment,StandardServletEnvironment类继承了StandardEnvironment并实现了ConfigurableWebEnvironment接口,StandardEnvironment继承了AbstractEnvironment
Spring Boot 属性加载原理解析
在创建ApplicationServletEnvironment的时候,会先创建父类的构造器,所以会先执行AbstractEnvironment的构造器,AbstractEnvironment是Environment的抽象基类

public AbstractEnvironment() {
	this(new MutablePropertySources());
}

MutablePropertySources 是PropertySources接口的默认实现,PropertySources是属性配置源接口,描述了如何获取属性值。

这里再调用了当前类的有参构造器。

protected AbstractEnvironment(MutablePropertySources propertySources) {
	this.propertySources = propertySources;
	//创建配置解析器
	this.propertyResolver = createPropertyResolver(propertySources);
	//调用自定义配置源,具体由子类实现
	customizePropertySources(propertySources);
}

protected void customizePropertySources(MutablePropertySources propertySources) {
}

这里就调用到了StandardServletEnvironmentcustomizePropertySources中:

protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
	propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
	if (jndiPresent && JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
		propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
	}
	super.customizePropertySources(propertySources);
}

在这里添加了关于ServletConfig、ServletContext、JNDI的配置源
Spring Boot 属性加载原理解析
在该方法的最后,又调用到了父类StandardEnvironmentcustomizePropertySources中:

protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(
			new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
	propertySources.addLast(
			new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

在这里添加了Java System属性、操作系统环境变量两个配置源
Spring Boot 属性加载原理解析
到此为止已经添加了4个配置源,由于这里不是JNDI环境,没有添加JNDI的配置源,这里执行结束后返回到SpringApplication的getOrCreateEnvironment()处
Spring Boot 属性加载原理解析
接着进入configureEnvironment(environment, applicationArguments.getSourceArgs())

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
	//这里用于添加转换服务
	if (this.addConversionService) {
		environment.setConversionService(new ApplicationConversionService());
	}
	//这里也是设置配置源,后面详解
	configurePropertySources(environment, args);
	//设置激活的配置文件
	configureProfiles(environment, args);
}

进入configurePropertySources(environment, args)

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
//获取环境中已有的配置源
	MutablePropertySources sources = environment.getPropertySources();
	//默认配置不为空,则添加到配置源中,defaultProperties通过springApplication.setDefaultProperties(properties) 配置
	if (!CollectionUtils.isEmpty(this.defaultProperties)) {
	//addOrMerge会判断已有的配置源中是否已经存在了defaultProperties,来判断是合并还是直接添加
		DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
	}
	//判断是否有命令行参数,addCommandLineProperties表示是否允许添加命令行配置,默认为true,可通过setAddCommandLineProperties配置
	if (this.addCommandLineProperties && args.length > 0) {
	//命令行配置源名称
		String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
		//已有配置源中是否包含命令行配置源名称
		if (sources.contains(name)) {
			PropertySource<?> source = sources.get(name);
			CompositePropertySource composite = new CompositePropertySource(name);
			//创建一个具有新名称的组合配置源
			composite
				.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
			composite.addPropertySource(source);
			//使用新的替换原来的配置源
			sources.replace(name, composite);
		}
		else {
			//不包含就添加到已有源的最前面
			sources.addFirst(new SimpleCommandLinePropertySource(args));
		}
	}
}

SimpleCommandLinePropertySource 用于解析命令行参数并填充到CommandLineArgs中,解析规则为:

–optName[=optValue]
必须以“–”为前缀,并且可以指定值,也可以不指定值。如果指定了值,则名称和值必须用等号(“=”)分隔,不带空格。该值可以是空字符串(可选)。
有效示例有:
–foo
–foo=
–foo=“”
–foo=bar
–foo=“bar then baz”
–foo=bar,baz,biz
无效示例:
-foo
–foo bar
–foo = bar
–foo=bar --foo=baz --foo=biz

添加完命令行配置源有,进入configureProfiles(environment, args)中,开始设置激活的配置文件:

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
}

这是一个空的protected方法,可见需要子类去实现,这边没有SpringApplication的子类,也就不会在这里处理。
configureEnvironment处理完后,进入ConfigurationPropertySources.attach(environment)

public static void attach(Environment environment) {
	Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
	MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
	PropertySource<?> attached = getAttached(sources);
	if (attached == null || !isUsingSources(attached, sources)) {
		attached = new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
				new SpringConfigurationPropertySources(sources));
	}
	sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
	sources.addFirst(attached);
}

该处代码用于将ConfigurationPropertySourcesPropertySource类型的源添加到已有的配置源中,名称为configurationProperties
这里处理完后,会调用listeners.environmentPrepared(bootstrapContext, environment),通过EventPublishingRunListener发送ApplicationEnvironmentPreparedEvent事件,这块前面我们已经多次讲到过,这里不再复述,我们进入EnvironmentPostProcessorApplicationListener,其中的onApplicationEvent在收到ApplicationEnvironmentPreparedEvent事件后,执行onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event)

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	ConfigurableEnvironment environment = event.getEnvironment();
	SpringApplication application = event.getSpringApplication();
	for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
			event.getBootstrapContext())) {
		postProcessor.postProcessEnvironment(environment, application);
	}
}

getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext()) 会获取所有的EnvironmentPostProcessor实例,如根据本系列文章的Demo获取到的实例有:

Spring Boot 属性加载原理解析
我们主要关注如下几个,其他的忽略:

  • RandomValuePropertySourceEnvironmentPostProcessor: 添加RandomValuePropertySource 配置源,用来解析RandomValuePropertySource的随机值属性

  • SystemEnvironmentPropertySourceEnvironmentPostProcessor:将原来的SystemEnvironmentPropertySource替换为OriginAwareSystemEnvironmentPropertySource,以便能够跟踪每个属性的SystemEnvironmentOrigin

  • SpringApplicationJsonEnvironmentPostProcessor:添加嵌入在环境变量或系统属性中的SPRING_APPLICATION_JSON 的属性

  • CloudFoundryVcapEnvironmentPostProcessor:如果是Cloud Foundry平台,添加Cloud Foundry相关的配置源

  • ConfigDataEnvironmentPostProcessor:添加application.yml等配置源

  • DevToolsHomePropertiesPostProcessor:添加Devtools 全局配置的配置源

另外@PropertySource注解配置的加载是在刷新上下文中的ConfigurationClassPostProcessor类中处理,具体代码可见ConfigurationClassParser
Spring Boot 属性加载原理解析
17种属性配置的加载基本都在这里了,最后总结一下

总结

Spring Boot 属性加载原理解析

作者其他要推荐的文章,欢迎来学习:
Prometheus 系列文章

  1. Prometheus 的介绍和安装
  2. 直观感受PromQL及其数据类型
  3. PromQL之选择器和运算符
  4. PromQL之函数
  5. Prometheus 告警机制介绍及命令解读
  6. Prometheus 告警模块配置深度解析
  7. Prometheus 配置身份认证
  8. Prometheus 动态拉取监控服务
  9. Prometheus 监控云Mysql和自建Mysql

Grafana 系列文章,版本:OOS v9.3.1

  1. Grafana 的介绍和安装
  2. Grafana监控大屏配置参数介绍(一)
  3. Grafana监控大屏配置参数介绍(二)
  4. Grafana监控大屏可视化图表
  5. Grafana 查询数据和转换数据
  6. Grafana 告警模块介绍
  7. Grafana 告警接入飞书通知

Spring Boot Admin 系列文章来源地址https://www.toymoban.com/news/detail-485910.html

  1. Spring Boot Admin 参考指南
  2. SpringBoot Admin服务离线、不显示健康信息的问题
  3. Spring Boot Admin2 @EnableAdminServer的加载
  4. Spring Boot Admin2 AdminServerAutoConfiguration详解
  5. Spring Boot Admin2 实例状态监控详解
  6. Spring Boot Admin2 自定义JVM监控通知
  7. Spring Boot Admin2 自定义异常监控
  8. Spring Boot Admin 监控指标接入Grafana可视化

到了这里,关于Spring Boot 属性加载原理解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring Boot】Spring—加载监听器

    前几天的时候,项目里有一个需求,需要一个开关控制代码中是否执行一段逻辑,于是理所当然的在yml文件中配置了一个属性作为开关,再配合nacos就可以随时改变这个值达到我们的目的,yml文件中是这样写的: 程序中的代码也很简单,大致的逻辑就是下面这样,如果取到的

    2024年02月08日
    浏览(42)
  • Spring Boot 配置属性设置优先级

    文章首发地址 Spring Boot设计了非常特殊的加载指定属性文件(PropertySource)的顺序,以允许对属性值进行合理的覆盖。属性值会以下面的优先级进行设置。 home目录下的Devtools全局设置属性(~/.spring-boot-devtools.properties,条件是当devtools激活时)。 @TestPropertySource注解的测试用例

    2024年02月16日
    浏览(53)
  • 【Spring Boot】Spring Boot自动加载机制:简化应用程序的启动

    在微服务盛行的今天,快速搭建和启动应用程序变得至关重要。Spring Boot作为Java生态系统中主流的框架,其自动加载机制使得开发者能够快速构建和启动应用程序。本文将详细介绍Spring Boot的自动加载机制,并通过代码示例加以说明。 首先,我们要了解Spring Boot自动加载机制

    2024年02月11日
    浏览(34)
  • 深入了解 Spring Boot 的加载过程

    Spring Boot 的加载过程可以大致分为以下几个阶段: 类加载 :首先,Java 虚拟机会加载必要的类,包括 Spring Boot 的核心类和应用程序的相关类。 配置解析 :Spring Boot 会解析各种配置,如 application.properties 或 application.yml 文件中的配置。 自动配置 :根据应用程序的需求和配置

    2024年04月23日
    浏览(28)
  • Spring Boot 的系统配置文件加载顺序

    【理论介绍】 配置文件加载顺序: 1、项目根目录下的config目录。【优先级最高】 2、项目根目录。 3、classpath下的config目录。 4、classpath目录(新建项目时application.properties默认所在位置)。【优先级最低】 备注: 加载顺序:4 - 3 - 2 - 1。 优先级:逐渐减低(1234)。 【举例说

    2024年02月09日
    浏览(52)
  • 17.Spring Boot加载指定YML文件

    Spring Boot专栏目录(点击进入…) Spring Boot默认支持properties和yml配置文件的读取,前者格式简单,但是只支持键值对。如果需要表达列表,最好使用YAML格式。 Spring Boot支持自动加载约定名称的配置文件,仅支持指定路径下指定名称的配置文件;例如application.yml。当自定义指定

    2024年02月10日
    浏览(48)
  • Spring源码解析——IOC属性填充

    正文 doCreateBean() 主要用于完成 bean 的创建和初始化工作,我们可以将其分为四个过程: 最全面的Java面试网站 createBeanInstance() 实例化 bean populateBean() 属性填充 循环依赖的处理 initializeBean() 初始化 bean 第一个过程实例化 bean在前面一篇博客中已经分析完了,这篇博客开始分析

    2024年02月08日
    浏览(39)
  • Kotlin扩展函数与属性原理解析

    扩展函数可以方便地给现有类增加属性和方法而不改动类地代码。 反编译: 可以看出扩展函数实际上是生成了一个静态方法,并且将被扩展的类的对象传进了函数中。 由此我们可以知道: 我们在扩展函数中可以访问被扩展的类的函数与属性,但是不能访问私有的函数与属性

    2024年02月19日
    浏览(30)
  • Flink 的时间属性及原理解析

    Flink 的 API 大体上可以划分为三个层次:处于 最底层的 ProcessFunction 、 中间一层的 DataStream API 和 最上层的 SQL/Table API ,这三层中的每一层都非常依赖于时间属性。时间在 Flink 中的地位如下图所示: 时间属性是流处理中最重要的一个方面,是流处理系统的基石之一,贯穿这三

    2024年02月03日
    浏览(32)
  • Spring Boot如何实现配置文件的自动加载和刷新?

    在使用Spring Boot开发应用程序时,配置文件是非常重要的组成部分。在不同的环境中,我们可能需要使用不同的配置文件,例如在开发、测试和生产环境中使用不同的配置文件。而且,当我们更改配置文件时,我们希望应用程序能够自动加载和刷新配置文件,而无需重启应用

    2024年02月07日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包