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)
通过断点看到有下面几个监听者
选两个监听者分析
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
PropertiesPropertySourceLoader 解析 . properties 和.xml文件
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); } }