从头阅读:SpringBoot启动源码分析
一、将属性源添加到指定的环境RandomValuePropertySource.addToEnvironment(environment);该方法很简单,就是把随机值属性源添加到名称为systemEnvironment的属性源后面
作用:可以在配置文件里,配置获取随机数,如:random.int、random.long二、利用内部类Loader加载器去加载候选的属性源和配置激活的profile
1、构造了一个Loader对象,构造器参数分别是当前应用环境和资源加载器
2、调用方法load如下图所示:

接下来我们一行一行来分析每一句代码:
this.propertiesLoader = new PropertySourcesLoader();依旧见其名知其义,首先创建一个属性源加载器,它的无参构造器调用了重载构造器并接收一个易变的属性源MutablePropertySources(接口PropertySources的默认实现,用于操作属性源,调整属性源优先级顺序)。然后通过老手法去查找属性源加载器,即spring.factories中key为org.springframework.boot.env.PropertySourceLoader的值,这里获取到两个,一个是PropertiesPropertySourceLoader(解析文件扩展名为properties和xml的文件),另一个是YamlPropertySourceLoader(解析文件扩展名为yml和yaml的文件)。
this.activatedProfiles = false;设置profile为未激活状态。
this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());创建一个后入先出的队列。
this.processedProfiles = new LinkedList<Profile>();构建一个收集已经处理过的profile的集合。
Set<Profile> initialActiveProfiles = initializeActiveProfiles();初始化激活的profile,逻辑就是在当前环境下查找key有没有spring.profiles.active和spring.profiles.include的属性,我们这里都没有,所以直接返回了一个空的集合。
this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));获取未处理的profile并添加到队列中,因为我们当前环境中也没有设置,所以也返回了集合。
因为此时此刻,我们都还没设置profile,所以spring就构建了一个默认的profile(构建的时候已标识)并添加到队列中。
this.profiles.add(null);该代码貌似很屌的样子,源码中已经给出解释了,这里我们就不关公面前耍大刀了。
接下来就是循环队列里的profile了。
Profile profile = this.profiles.poll();这里取出的肯定就是null了,因为这就是前面刚故意加入的null。
getSearchLocations()获取搜索配置文件路径,如果环境中存在key为spring.config.location的属性,则将其添加到待搜索的集合中。因为我们这里没有,所以就直接使用了默认的搜索路径了,分别是classpath:/,classpath:/config/,file:./,file:./config/。
搜索路径优先级:file:./config/ > file:./ > classpath:/config/ > classpath:/
接下来就开始处理每一个路径了,首先是判断location是不是目录(以斜杠结尾的表示目录),如果是文件则直接处理该文件。因为我们这里都不是,所以需要拼上配置文件名。getSearchNames()就是获取配置文件名,如果当前环境中存在spring.config.name属性值,则添加到待搜索集合,因为我们这里没有设置,所以依然使用的是默认文件名即application。因为我的配置文件放在classpath:/config/ ,所以我就直接跳到第三个路径讲解。我们来看私有方法load如下:

前面比较简单,我们直接进入方法doLoadIntoGroup,在解析完配置文件后,调用了handleProfileProperties方法,一看就是处理profile的,因为我们可能在默认的配置文件里设置 spring.profiles.active。

SpringProfiles springProfiles = bindSpringProfiles(propertySource);就是从当前解析的属性源中获取spring.profiles.active和spring.profiles.include的值并设置到SpringProfiles对象上,我这里的active是local(表示本地环境启动)
maybeActivateProfiles(springProfiles.getActiveProfiles());这里首先判断profile是否已经激活,没激活则把默认配置文件中解析的local添加到profile队列中,同时将标志位activatedProfiles置为true表示profile已经激活,最后再移除没有处理的默认profile。
划重点:
这里就解释了,为什么spring要在前面加个null,为什么在循环第二个profile的时候,profile不是default却变成了我们在默认配置文件里设置的local。
this.processedProfiles.add(profile);将当前已处理完的profile加入到已处理的profile集合中。这里就解释了,为什么spring要在前面加个null,为什么在循环第二个profile的时候,profile不是default却变成了我们在默认配置文件里设置的local。
当第二次从队列中取出profile时,队列已经空了,自然这个while循环也就结束了。
接下来的处理,其实和第一次几乎没什么两样,唯一不同的是,此时profile是有值的,所以最终解析的配置文件名就变成下图所示的样子:

这也解释了为什么配置文件中间要加一个 " - "了。
loadIntoGroup(group, location + name + "-" + profile + "." + ext,profile);这里是为了解决那些把profile写在和配置文件名一样的人,如:你命名了一个application-dev.ym里面又存在一个spring.profiles.active=dev,看来官方也是良苦用心啊!
三、将解析的属性源添加到环境中
addConfigurationProperties(this.propertiesLoader.getPropertySources());
这里大致就是构建一个名为applicationConfigurationProperties的属性源,然后添加到MutablePropertySources中,这里使用的addLast,所以当前它的优先级是最低的。
此时环境里属性源如下图所示:
