述
上文中了解了如何通过 Spring Aware 来获取属性,最终其实是使用 Environment
对象来获取系统中的属性的,那么 Environment
对象是怎么来的, Spring Boot 是如何将我们的属性配置,转成一个 Environment
对象的, 下面来看一下获取 Environment
对象的源码
源码解析
还是先进入项目的run方法,找到关于 Environment
的部分,如下:
进入这个方法看一下, 先大致了解一下这个方法做的事情
然后我们就围绕着这个方法的流程,看一下他这里的每一步都怎么处理的
getOrCreateEnvironment()
进入第一个 getOrCreateEnvironment()
方法,如下:
然后我们看一下获取到的这个 StandardServletEnvironment
对象的类图
可以看到这里先继承了 StandardEnvironment
然后 StandardEnvironment
又继承了 AbstractEnvironment
,所以这里这个构造首先是会去调用父类的构造,也就是 AbstractEnvironment
, 我们进去这个类看一下他的构造方法
这里调用了 customizePropertySources
方法,传入了 propertySources
,继续点进去看一下
可以看到这里是一个空的实现, 具体的实现由子类完成,我们创建的子类是 StandardServletEnvironment
,所以这里会调用 StandardServletEnvironment
的 customizePropertySources()
方法,进入
这里最后调用的父类就是 StandardEnvironment.customizePropertySources()
这个方法
综上所述,这里 new StandardServletEnvironment()
就是添加了几个属性源
configureEnvironment()
回到 prepareEnvironment()
方法,再来看下面的 configureEnvironment()
这个方法
这里又调用了两个方法 configurePropertySources()
和 configureProfiles()
, 分别看一下
configurePropertySources()
这里来看一下这个 SimpleCommandLinePropertySource
的构造
进入 parse()
方法
所以这个方法就是用来解析命令行的配置,然后添加到属性源中
configureProfiles()
这里这个集合的用处我们之后再说
ConfigurationPropertySources.attach
继续往下看 prepareEnvironment
中的 ConfigurationPropertySources.attach
, 点进去,如下:
environmentPrepared
上面的流程都执行完成之后,会发送一个 ApplicationEnvironmentPreparedEvent
事件,这里就会调用 Spring Boot 的广播器发送,具体逻辑我们在监听器的那里已经看过了, 最后会进入 SimpleApplicationEventMulticaster.multicastEvent()
这个方法发送
我们这里打个断点进去,如下
通过 getApplicationListeners()
这个方法,拿到所有对这个事件感兴趣的监听器然后依次调用他们的 onApplicationEvent()
方法
然后如图中,这里一共有 7 个监听器对这个事件感兴趣,我们这里重点关注第一个, 也就是 ConfigFileApplicationListener
,然后进去看一下他的 onApplicationEvent()
这个方法
ConfigFileApplicationListener
这里主要是通过 SpringFactoriesLoader
找到所有的 EnvironmentPostProcessor
, 然后会把当前类 ConfigFileApplicationListener
也放到 EnvironmentPostProcessor
的集合中,因为 ConfigFileApplicationListener
也实现了 EnvironmentPostProcessor
这个接口
拿到之后做个排序,我们来看一下他一共有哪些 EnvironmentPostProcessor
这里一共找到了 5 个 EnvironmentPostProcessor
的实现,我们重点关注前面4个
SystemEnvironmentPropertySourceEnvironmentPostProcessor
断点继续往下走,先看第一个 SystemEnvironmentPropertySourceEnvironmentPostProcessor
这个类就是把之前的命令行的数据源转换成了一个新的对象,然后把之前的替换掉,实际属性并没有新增或者删除
SpringApplicationJsonEnvironmentPostProcessor
下一个是 SpringApplicationJsonEnvironmentPostProcessor
这里进入上面 JsonPropertyValue.get
这个方法看一下
这个类做的事情其实就是解析通过 SPRING_APPLICATION_JSON
这种方式指定的属性,最后添加到属性源中去
CloudFoundryVcapEnvironmentPostProcessor
进入这个类看一下源码
这里是 Spring Cloud 环境中设置的一些属性,当前环境并不是 cloud 环境,所以不做详细分析
ConfigFileApplicationListener
最后就是当前类 ConfigFileApplicationListener
这里首先添加了个随机数的属性源,然后创建了一个 Loader
对象,调用 load()
方法,我们点进去看一下
这部分是解析我们的配置文件,之后再详细分析
所以这个类做的事情就是添加随机数属性源,然后解析配置文件
bindToSpringApplication()
回到 prepareEnvironment()
方法中,继续往下看一下 bindToSpringApplication()
这个方法
这个方法作用就是将环境变量中的 spring.main
绑定到 springApplication
这个类中
到这儿, prepareEnvironment()
这个方法做的事情我们就分析完了
ConfigurationClassParser
之后我们再来看一个类 ConfigurationClassParser
,这个类的作用主要是用来解析 @PropertySource
配置的属性集,源码如下
这里就先大致的看一下,后面再去详细介绍
总结
本文,我们了解了 Spring Boot 的属性加载, 分析了 prepareEnvironment()
这个方法,然后我们来做一个总结
getOrCreateEnvironment()
方法主要做的事情是:
- 添加
servletConfigInitParams
属性集 - 添加
servletContextInitParams
属性集 - 添加Jndi属性集
- 添加
systemProperties
属性集 - 添加
systemEnvironment
属性集
configureEnvironment()
方法主要做的事情是:
- 添加
defaultProperties
属性集 - 添加
commandLineArgs
属性集
listeners.environmentPrepared()
方法主要做的事情:
- 添加
SPRING_APPLICATION_JSON
属性集 - 添加
vcap
属性集,前提是在 Spring Cloud 的环境下 - 添加
random
属性集 - 添加
application-profile
属性集
ConfigurationPropertySources.attach()
方法做的事情:
- 添加
configurationProperties
属性集
ConfigurationClassParser
做的事情:
- 添加
@PropertySource
属性集