spring boot参数配置之Environment源码分析

在spring boot中所有参数都被理解为当前项目运行的环境变量。spring boot环境的初始化操作在ConfigurableApplicationContext的创建之前,功实现主要依赖PropertyResolverPropertySource这两个接口。

1. PropertyResolver接口

spring boot项目的非web项目会创建StandardEnvironment类并完成初始化参数的加载,web项目中则创建的为StandardServletEnvironment类来完成参数的初始化,其类层次关系如下
在这里插入图片描述

  • PropertyResolver:提供属性访问功能。
  • ConfigurablePropertyResolver :继承自PropertyResolver,额外主要提供属性类型转换(基于org.springframework.core.convert.ConversionService)功能。
  • Environment:继承自PropertyResolver,额外提供访问和判断profiles的功能。
  • ConfigurableEnvironment:继承自ConfigurablePropertyResolverEnvironment,并且提供设置激活的profile和默认的profile的功能。
  • ConfigurableWebEnvironment:继承自ConfigurableEnvironment,并且提供配置Servlet上下文和Servlet参数的功能。
  • AbstractEnvironment:实现了ConfigurableEnvironment接口,默认属性和存储容器的定义,并且实现了ConfigurableEnvironment种的方法,并且为子类预留可覆盖了扩展方法。
  • StandardEnvironment:继承自AbstractEnvironment,非Servlet(Web)环境下的标准Environment实现。
  • StandardServletEnvironment:继承自StandardEnvironment,Servlet(Web)环境下的标准Environment实现。

2. PropertySource接口

PropertySource为真实参数存储的地方,Environment的静态属性和存储容器都是在AbstractEnvironment中定义的,ConfigurableWebEnvironment接口提供的getPropertySources()方法可以获取到返回的MutablePropertySources实例,然后添加额外的PropertySource。实际上,Environment的存储容器就是org.springframework.core.env.PropertySource的子类集合,如下图:
PropertySource的常用类层次关系如下:
在这里插入图片描述

  • MapPropertySourcesource指定为Map实例的PropertySource实现。
  • PropertiesPropertySourcesource指定为Map实例的PropertySource实现,内部的Map实例由Properties实例转换而来。
  • ResourcePropertySource:继承自PropertiesPropertySourcesource指定为通过Resource实例转化为Properties再转换为Map实例。
  • StubPropertySourcePropertySource的一个内部类,source设置为null,实际上就是空实现。
  • OriginTrackedMapPropertySource:获取value值时,如果value为OriginTrackedValue,则调用value的getValue方法循环一直找到最原始的值

3. 环境初始化代码分析

  1. 从SpringApplication.run入手
public ConfigurableApplicationContext run(String... args) {
    
    
 …………
   try {
    
    
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      //Environment创建和初始化地方,在ApplicationContext创建之前
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] {
    
     ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
    …………
   }
   catch (Throwable ex) {
    
    
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }
…………
}
…………
private ConfigurableEnvironment prepareEnvironment(
      SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
    
    
   // 根据webApplicationType来创建StandardServletEnvironment和StandardEnvironment,并初始化一些基础的系统的PropertySource,
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   listeners.environmentPrepared(environment);
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
    
    
      environment = new EnvironmentConverter(getClassLoader())
            .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

getOrCreateEnvironment()执行完成后environment所初始化的PropertySource如下:
在这里插入图片描述

  1. 断点进入listeners.environmentPrepared(environment), 进入SpringApplicationRunListeners类
public void environmentPrepared(ConfigurableEnvironment environment) {
    
    
   for (SpringApplicationRunListener listener : this.listeners) {
    
    
      listener.environmentPrepared(environment);
   }
}

这里的listeners就一个EventPublishingRunListener,进入EventPublishingRunListenerenvironmentPrepared方法,

public void environmentPrepared(ConfigurableEnvironment environment) {
    
    
   this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
         this.application, this.args, environment));
}

可以看到实则广播了ApplicationEnvironmentPreparedEvent事件,该事件被ConfigFileApplicationListener所响应

public class ConfigFileApplicationListener
      implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    
    
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    
    
   return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType)
         || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
    
    
   if (event instanceof ApplicationEnvironmentPreparedEvent) {
    
    
      onApplicationEnvironmentPreparedEvent(
            (ApplicationEnvironmentPreparedEvent) event);
   }
   if (event instanceof ApplicationPreparedEvent) {
    
    
      onApplicationPreparedEvent(event);
   }
}
…………
}

重点关注onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event)方法:

private void onApplicationEnvironmentPreparedEvent(
      ApplicationEnvironmentPreparedEvent event) {
    
    
   List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
   postProcessors.add(this);
   AnnotationAwareOrderComparator.sort(postProcessors);
   for (EnvironmentPostProcessor postProcessor : postProcessors) {
    
    
      postProcessor.postProcessEnvironment(event.getEnvironment(),
            event.getSpringApplication());
   }
}

这里的postProcessors包含以下6个postProcessors,分别以6种不同的方法来加载不同场景下的参数配置:
在这里插入图片描述
重点介绍一下ConfigFileApplicationListener这个 postProcessors,主要负责加载spring boot 默认预设的配置文件如:classpath:/,classpath:/config/,file:./,file:./config/ 目录下的 application.propertiesapplication.yml文件,如果在不同的目录中存在多个配置文件,它的读取顺序是:

  1. config/application.properties(项目根目录中config目录下)
  2. config/application.yml
  3. application.properties(项目根目录下)
  4. application.yml
  5. resources/config/application.properties(项目resources目录中config目录下)
  6. resources/config/application.yml
  7. resources/application.properties(项目的resources目录下)
  8. resources/application.yml

onApplicationEnvironmentPreparedEvent执行完6个postProcessors后可以看到environmentPropertySource又增加了:
在这里插入图片描述
第6个为项目跟目录下的自定义配置,断点继续执行,回到SpringApplication.prepareEnvironment(……)中,接着执行bindToSpringApplication(environment)将environment绑定到上下文中。

到此为止environment的参数加载完毕,开始Banner信息的输出,并创建ApplicationContext对象。

猜你喜欢

转载自blog.csdn.net/mapleleafforest/article/details/90180231