Spring Boot-12-属性加载解析

上文中了解了如何通过 Spring Aware 来获取属性,最终其实是使用 Environment 对象来获取系统中的属性的,那么 Environment 对象是怎么来的, Spring Boot 是如何将我们的属性配置,转成一个 Environment 对象的, 下面来看一下获取 Environment 对象的源码

源码解析

还是先进入项目的run方法,找到关于 Environment 的部分,如下:

image

进入这个方法看一下, 先大致了解一下这个方法做的事情

image

然后我们就围绕着这个方法的流程,看一下他这里的每一步都怎么处理的

getOrCreateEnvironment()

进入第一个 getOrCreateEnvironment() 方法,如下:

image

然后我们看一下获取到的这个 StandardServletEnvironment 对象的类图

image

可以看到这里先继承了 StandardEnvironment 然后 StandardEnvironment 又继承了 AbstractEnvironment,所以这里这个构造首先是会去调用父类的构造,也就是 AbstractEnvironment, 我们进去这个类看一下他的构造方法

image

这里调用了 customizePropertySources 方法,传入了 propertySources,继续点进去看一下

image

可以看到这里是一个空的实现, 具体的实现由子类完成,我们创建的子类是 StandardServletEnvironment ,所以这里会调用 StandardServletEnvironmentcustomizePropertySources() 方法,进入

image

这里最后调用的父类就是 StandardEnvironment.customizePropertySources() 这个方法

image

综上所述,这里 new StandardServletEnvironment() 就是添加了几个属性源

configureEnvironment()

回到 prepareEnvironment() 方法,再来看下面的 configureEnvironment() 这个方法

image

这里又调用了两个方法 configurePropertySources()configureProfiles(), 分别看一下

configurePropertySources()

image

这里来看一下这个 SimpleCommandLinePropertySource 的构造

image

进入 parse() 方法

image

所以这个方法就是用来解析命令行的配置,然后添加到属性源中

configureProfiles()

image

这里这个集合的用处我们之后再说

ConfigurationPropertySources.attach

继续往下看 prepareEnvironment 中的 ConfigurationPropertySources.attach, 点进去,如下:

image

environmentPrepared

上面的流程都执行完成之后,会发送一个 ApplicationEnvironmentPreparedEvent 事件,这里就会调用 Spring Boot 的广播器发送,具体逻辑我们在监听器的那里已经看过了, 最后会进入 SimpleApplicationEventMulticaster.multicastEvent() 这个方法发送

我们这里打个断点进去,如下

image

通过 getApplicationListeners() 这个方法,拿到所有对这个事件感兴趣的监听器然后依次调用他们的 onApplicationEvent() 方法

然后如图中,这里一共有 7 个监听器对这个事件感兴趣,我们这里重点关注第一个, 也就是 ConfigFileApplicationListener ,然后进去看一下他的 onApplicationEvent() 这个方法

ConfigFileApplicationListener

image

这里主要是通过 SpringFactoriesLoader 找到所有的 EnvironmentPostProcessor, 然后会把当前类 ConfigFileApplicationListener 也放到 EnvironmentPostProcessor 的集合中,因为 ConfigFileApplicationListener 也实现了 EnvironmentPostProcessor 这个接口

拿到之后做个排序,我们来看一下他一共有哪些 EnvironmentPostProcessor

image

这里一共找到了 5 个 EnvironmentPostProcessor 的实现,我们重点关注前面4个

SystemEnvironmentPropertySourceEnvironmentPostProcessor

断点继续往下走,先看第一个 SystemEnvironmentPropertySourceEnvironmentPostProcessor

image

这个类就是把之前的命令行的数据源转换成了一个新的对象,然后把之前的替换掉,实际属性并没有新增或者删除

SpringApplicationJsonEnvironmentPostProcessor

下一个是 SpringApplicationJsonEnvironmentPostProcessor

image

这里进入上面 JsonPropertyValue.get 这个方法看一下

image

这个类做的事情其实就是解析通过 SPRING_APPLICATION_JSON 这种方式指定的属性,最后添加到属性源中去

image

CloudFoundryVcapEnvironmentPostProcessor

进入这个类看一下源码

image

这里是 Spring Cloud 环境中设置的一些属性,当前环境并不是 cloud 环境,所以不做详细分析

ConfigFileApplicationListener

最后就是当前类 ConfigFileApplicationListener
image

这里首先添加了个随机数的属性源,然后创建了一个 Loader 对象,调用 load() 方法,我们点进去看一下

image

这部分是解析我们的配置文件,之后再详细分析

所以这个类做的事情就是添加随机数属性源,然后解析配置文件

bindToSpringApplication()

回到 prepareEnvironment() 方法中,继续往下看一下 bindToSpringApplication() 这个方法

image

这个方法作用就是将环境变量中的 spring.main 绑定到 springApplication 这个类中

到这儿, prepareEnvironment() 这个方法做的事情我们就分析完了

ConfigurationClassParser

之后我们再来看一个类 ConfigurationClassParser ,这个类的作用主要是用来解析 @PropertySource 配置的属性集,源码如下

image

image

这里就先大致的看一下,后面再去详细介绍

总结

本文,我们了解了 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 属性集