0
点赞
收藏
分享

微信扫一扫

【Spring Boot 四】启动之准备系统环境environmentPrepared


SpringBoot启动的时候 ​​listeners.starting()​​ ;接下来就是准备环境的过程

environmentPrepared


系统环境已经准备就绪


private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
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;
}

上面代码总结来说分为下面几步

  • 创建 ConfigurableEnvironment ; 增加至少(根据启动类型不同,可能还会增加其他的属性源)两个属性源 一个Jvm属性源;一个环境变量属性源
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()))
  • 增加命令行属性源 (添加到最前面,优先级最高); 就是启动时候的那个入参Args;详情看​​SpringBoot 一 SpringApplication启动类的Args详解​​​sources.addFirst(new SimpleCommandLinePropertySource(args));​
  • 触发​​listeners.environmentPrepared(environment)​​事件
  • bindToSpringApplication

上面几个步骤我们主要分析一下

环境准备就绪事件通知listeners.environmentPrepared(environment)

通过断点看到有下面几个监听者

【Spring Boot 四】启动之准备系统环境environmentPrepared_spring boot

选两个监听者分析

  • ​ConfigFileApplicationListener​
  • ​DelegatingApplicationListener​

ConfigFileApplicationListener

事件通知到之后 是执行了下面的方法

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());
}
}
  • 通过​​spring.factories​​方式加载​​EnvironmentPostProcessor​​实现类;
  • 自身也是一个​​EnvironmentPostProcessor​
  • 将上述EnvironmentPostProcessor排序之后执行​​postProcessEnvironment​​方法

【Spring Boot 四】启动之准备系统环境environmentPrepared_加载_02

SystemEnvironmentPropertySourceEnvironmentPostProcessor


将之前加载的系统属性对象 替换陈给一个新的对象;但是属性; 这样做的原因TODO
【Spring Boot 四】启动之准备系统环境environmentPrepared_加载_03
【Spring Boot 四】启动之准备系统环境environmentPrepared_配置文件_04


ConfigFileApplicationListener


作为一个EnvironmentPostProcessor的时候,他的作用是想environment中添加了一个​​RandomValuePropertySource​​​属性源; 可以通过​​environment.getProperty("random.*")​​返回各种随机值


​RandomValuePropertySource​​用法

environment.getProperty("random.int")
environment.getProperty("random.long")
environment.getProperty("random.int.5,100;") 5~100中随机(后面的;要接上,因为它会截掉最后一个字符;)
environment.getProperty("random.long.5,10000;") 5~10000中随机(后面的;要接上,因为它会截掉最后一个字符;)
environment.getProperty("random.uuid")

​new Loader(environment, resourceLoader).load();​​ 加载 配置文件中的属性

Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader
: new DefaultResourceLoader();
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
}

【Spring Boot 四】启动之准备系统环境environmentPrepared_配置文件_05

配置文件加载器有两个 他们都 实现了 接口 ​​PropertySourceLoader​

  1. ​PropertiesPropertySourceLoader​​ 解析 . properties 和.xml文件
  2. ​YamlPropertySourceLoader​​ 解析 .yml 和 .yaml文件

加载load()

public void load() {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
resetEnvironmentProfiles(this.processedProfiles);
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}

简单概述一下这个Loader.load()方法做了什么

1.初始化配置文件 ​​initializeProfiles​​; 读取属性​​spring.profiles.include; spring.profiles.active​​的值添加到使用的配置文件属性

2. 将1读取到的配置文件解析加载属性到Environment中【Spring Boot 四】启动之准备系统环境environmentPrepared_加载_06

看图中最后两个就是属性源就是加载完配置文件后添加的

Binder


简单来说,就是将Environment中的属性值,绑定到某个对象中去的;比如SpringApplication中的属性bannerMode 默认是 ​​Banner.Mode.CONSOLE​​ 但是我在配置文件中​​spring.main.banner-mode=log​​ 将它改成log形式,为啥修改可以成功呢?是因为在启动过程中执行了下面的代码
【Spring Boot 四】启动之准备系统环境environmentPrepared_加载_07
他会将spring.main开头的配置都会绑定到 ​​Bindable.ofInstance(this)​​中 这个this就是​​SpringApplication​


Springboot 2.x新引入的类,负责处理对象与多个ConfigurationPropertySource(属性)之间的绑定。

比Environment类好用很多,可以非常方便地进行类型转换,以及提供回调方法介入绑定的各个阶段进行深度定制。

以前获取属性值是 用Environment中的方法,现

# 判断是否包含键值
boolean containsProperty(String key);

# 获取属性值,如果获取不到返回null
String getProperty(String key);

# 获取属性值,如果获取不到返回缺省值
String getProperty(String key, String defaultValue);

# 获取属性对象;其转换和Converter有关,会根据sourceType和targetType查找转换器
<T> T getProperty(String key, Class<T> targetType)

现在用

SpringBoot属性绑定Environment和Binder

【Spring Boot 四】启动之准备系统环境environmentPrepared_spring boot_08

DelegatingApplicationListener


从环境中读取属性 ​​context.listener.classes​​​;的值 并且将它们都实例化; 这些calss必须是​​ApplicationListener​​的实现类; 实例化好了之后加入到监听器列表;这是另一种实现 自定义监听与通知的 方式; ​​SpringBoot 自定义监听与通知​​


bindToSpringApplication


将 environment中的spring.main开头的属性 绑定到SpringApplication 中的属性值上


protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}

【Spring Boot 四】启动之准备系统环境environmentPrepared_spring_09

【Spring Boot 四】启动之准备系统环境environmentPrepared_加载_10



举报

相关推荐

0 条评论