spring应用在启动时会调用SpringApplication.java类中的prepareEnvironment()方法来加载配置文件。
- 创建环境:根据应用类型判断是SERVLET->StandardServletEnvironment还是REACTIVE->StandardReactiveWebEnvironment。在SpringApplication类实例化的时候通过WebApplicationType.deduceFromClasspath()判断当前的应用类型。如果
org.springframework.web.reactive.DispatcherHandler
类存在则是WebApplicationType.REACTIVE;如果javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext都不存在则WebApplicationType.NONE,即啥都不是,相反当有一个存在就是WebApplicationType.SERVLET。确定了应用类型后就创建StandardServletEnvironment的实例对象。 - 为环境进行配置:设置类型转换服务(ApplicationConversionService.sharedInstance)。
- 将命令行参数封装成SimpleCommandLinePropertySource对象。
- 遍历命令行参数,即主类中main接受的String[]
- 将每个参数添加到CommandLineArgs的属性nonOptionArgs(ArrayList)中
- 创建SimpleCommandLinePropertySource(extends PropertySource)对象,name为commandLineArgs,source为CommandLineArgs
- 添加profiles
- 将命令行参数封装成SimpleCommandLinePropertySource对象。
- 创建名为configurationProperties的配置属性源ConfigurationPropertySourcesPropertySource
- 发布ApplicationEnvironmentPreparedEvent事件,
ConfigFileApplicationListener
监听器会处理这个事件。 ConfigFileApplicationListener
监听器会加载环境后置处理器EnvironmentPostProcessor:到spring.factories中找到EnvironmentPostProcessor接口的实现类进行实例化,并将ConfigFileApplicationListener本身加入,之后依次调用每个处理器进行处理
SystemEnvironmentPropertySourceEnvironmentPostProcessor
获取"systemEnvironment"属性源,即系统环境变量,封装成SystemEnvironmentPropertySource对象保存下来
SpringApplicationJsonEnvironmentPostProcessor
处理json格式的属性源
ConfigFileApplicationListener
重头戏来了,就是这个类处理配置文件的加载的。
- 创建个Loader加载器,加载器内部持有占位符解析器,解析${};通过spring.factories加载属性源加载器,分别是PropertiesPropertySourceLoader(properties/xml)和YamlPropertySourceLoader(yaml/yml),调用load()方法进行加载。
- 到"classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/"这些路径下加载配置文件。
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
//getSearchLocations()方法返回:"classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/"
getSearchLocations().forEach((location) -> {
boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
- 默认加载的分别是application开头、default开头的文件名,使用刚刚从spring.factories加载的属性源加载器来解读配置文件。到每个路径下获取Resource资源getResources(location)。使用applicationConfig: [classpath:/application.properties]表示获取到的属性源。读取对应的配置文件,获取每个配置。
- 创建CharacterReader读取每个字符
- 读取配置文件中的key,读取每个字符到’=‘或’:'为止,将读到的字符buffer.append下来,最终得到的字符串为key
- 在没有读取到换行符时继续读取每个字符,将读到的字符buffer.append下来,最终得到的字符串为value
- 将读取的key和value添加到LinkedHashMap
- 加载了文件以后将缓存下来,将读取到的属性源添加到环境中
这样就完成了配置源的加载,最终所有的属性都保存在StandardServletEnvironment的属性propertySources中,可以通过this.environment.getPropertySources()获取到。当获取某个具体的配置时,对所有的配置源进行遍历,通过key获取具体的值。