0


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,属性加载就是在这一步开始的。

privateConfigurableEnvironmentprepareEnvironment(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.");//绑定环境到SpringApplicationbindToSpringApplication(environment);//非自定义环境配置,就将其转换为标准类型if(!this.isCustomEnvironment){EnvironmentConverter environmentConverter =newEnvironmentConverter(getClassLoader());
        environment = environmentConverter.convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}//重新将ConfigurationPropertySource支持附加到指定的环境ConfigurationPropertySources.attach(environment);return environment;}

进入

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

由于没有显示设置,使用的是默认的ApplicationContextFactory

private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT; 
ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();

进入

createEnvironment(this.webApplicationType)

中:

publicConfigurableEnvironmentcreateEnvironment(WebApplicationType webApplicationType){returngetFromSpringFactories(webApplicationType,ApplicationContextFactory::createEnvironment,null);}

进入

getFromSpringFactories

中:

private<T>TgetFromSpringFactories(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定义了

ReactiveWebServerApplicationContextFactory

ServletWebServerApplicationContextFactory

,所以在这里会分别去调用其中的

createEnvironment

方法,由于这边是web环境,进入ServletWebServerApplicationContextFactory的createEnvironment中。

publicConfigurableEnvironmentcreateEnvironment(WebApplicationType webApplicationType){//不是Web Servlet环境的话返回null,是的话创建一个ApplicationServletEnvironmentreturn(webApplicationType !=WebApplicationType.SERVLET)?null:newApplicationServletEnvironment();}

进入ApplicationServletEnvironment类中,其继承了StandardServletEnvironment,StandardServletEnvironment类继承了StandardEnvironment并实现了ConfigurableWebEnvironment接口,StandardEnvironment继承了AbstractEnvironment
在这里插入图片描述
在创建ApplicationServletEnvironment的时候,会先创建父类的构造器,所以会先执行AbstractEnvironment的构造器,AbstractEnvironment是Environment的抽象基类

publicAbstractEnvironment(){this(newMutablePropertySources());}

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

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

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

这里就调用到了

StandardServletEnvironment

customizePropertySources

中:

protectedvoidcustomizePropertySources(MutablePropertySources propertySources){
    propertySources.addLast(newStubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
    propertySources.addLast(newStubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));if(jndiPresent &&JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()){
        propertySources.addLast(newJndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));}super.customizePropertySources(propertySources);}

在这里添加了关于

ServletConfig、ServletContext、JNDI

的配置源
在这里插入图片描述
在该方法的最后,又调用到了父类

StandardEnvironment

customizePropertySources

中:

protectedvoidcustomizePropertySources(MutablePropertySources propertySources){
    propertySources.addLast(newPropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,getSystemProperties()));
    propertySources.addLast(newSystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,getSystemEnvironment()));}

在这里添加了

Java System属性、操作系统环境变量

两个配置源
在这里插入图片描述
到此为止已经添加了4个配置源,

由于这里不是JNDI环境,没有添加JNDI的配置源

,这里执行结束后返回到SpringApplication的getOrCreateEnvironment()处
在这里插入图片描述
接着进入

configureEnvironment(environment, applicationArguments.getSourceArgs())

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

进入

configurePropertySources(environment, args)

protectedvoidconfigurePropertySources(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 =newCompositePropertySource(name);//创建一个具有新名称的组合配置源
            composite
                .addPropertySource(newSimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
            composite.addPropertySource(source);//使用新的替换原来的配置源
            sources.replace(name, composite);}else{//不包含就添加到已有源的最前面
            sources.addFirst(newSimpleCommandLinePropertySource(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)

中,开始设置激活的配置文件:

protectedvoidconfigureProfiles(ConfigurableEnvironment environment,String[] args){}

这是一个空的protected方法,可见需要子类去实现,这边没有SpringApplication的子类,也就不会在这里处理。

configureEnvironment

处理完后,进入

ConfigurationPropertySources.attach(environment)

publicstaticvoidattach(Environment environment){Assert.isInstanceOf(ConfigurableEnvironment.class, environment);MutablePropertySources sources =((ConfigurableEnvironment) environment).getPropertySources();PropertySource<?> attached =getAttached(sources);if(attached ==null||!isUsingSources(attached, sources)){
        attached =newConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,newSpringConfigurationPropertySources(sources));}
    sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
    sources.addFirst(attached);}

该处代码用于将

ConfigurationPropertySourcesPropertySource

类型的源添加到已有的配置源中,名称为

configurationProperties

这里处理完后,会调用

listeners.environmentPrepared(bootstrapContext, environment)

,通过

EventPublishingRunListener

发送ApplicationEnvironmentPreparedEvent事件,这块前面我们已经多次讲到过,这里不再复述,我们进入

EnvironmentPostProcessorApplicationListener

,其中的

onApplicationEvent

在收到ApplicationEnvironmentPreparedEvent事件后,执行

onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event)
privatevoidonApplicationEnvironmentPreparedEvent(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获取到的实例有:

在这里插入图片描述
我们主要关注如下几个,其他的忽略:

  • RandomValuePropertySourceEnvironmentPostProcessor: 添加RandomValuePropertySource 配置源,用来解析RandomValuePropertySource的随机值属性
  • SystemEnvironmentPropertySourceEnvironmentPostProcessor:将原来的SystemEnvironmentPropertySource替换为OriginAwareSystemEnvironmentPropertySource,以便能够跟踪每个属性的SystemEnvironmentOrigin
  • SpringApplicationJsonEnvironmentPostProcessor:添加嵌入在环境变量或系统属性中的SPRING_APPLICATION_JSON 的属性
  • CloudFoundryVcapEnvironmentPostProcessor:如果是Cloud Foundry平台,添加Cloud Foundry相关的配置源
  • ConfigDataEnvironmentPostProcessor:添加application.yml等配置源
  • DevToolsHomePropertiesPostProcessor:添加Devtools 全局配置的配置源

另外

@PropertySource注解配置

的加载是在

刷新上下文

中的

ConfigurationClassPostProcessor

类中处理,具体代码可见

ConfigurationClassParser


在这里插入图片描述
17种属性配置的加载基本都在这里了,最后总结一下

总结

在这里插入图片描述

作者其他要推荐的文章,欢迎来学习:
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 系列

  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 java spring

本文转载自: https://blog.csdn.net/weixin_40972073/article/details/131205549
版权归原作者 阿提说说 所有, 如有侵权,请联系我们删除。

“Spring Boot 属性加载原理解析”的评论:

还没有评论