写点什么

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

  • 2022 年 8 月 05 日
  • 本文字数:3304 字

    阅读完需:约 11 分钟

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

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

1、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)

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

选两个监听者分析

  • 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方法

在这里插入图片描述

SystemEnvironmentPropertySourceEnvironmentPostProcessor

将之前加载的系统属性对象 替换陈给一个新的对象;但是属性; 这样做的原因 TODO



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());  }
复制代码


配置文件加载器有两个 他们都 实现了 接口 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 中

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

Binder

简单来说,就是将 Environment 中的属性值,绑定到某个对象中去的;比如 SpringApplication 中的属性 bannerMode 默认是 Banner.Mode.CONSOLE 但是我在配置文件中spring.main.banner-mode=log 将它改成 log 形式,为啥修改可以成功呢?是因为在启动过程中执行了下面的代码

他会将 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


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);  } }
复制代码


在这里插入图片描述

在这里插入图片描述

发布于: 刚刚阅读数: 2
用户头像

关注公众号: 石臻臻的杂货铺 获取最新文章 2019.09.06 加入

进高质量滴滴技术交流群,只交流技术不闲聊 加 szzdzhp001 进群 也可获取《kafka运维大全》

评论

发布
暂无评论
【Spring Boot 四】启动之准备系统环境environmentPrepared_Spring Boot_石臻臻的杂货铺_InfoQ写作社区