前言

在上一篇中了解了spring配置资源的加载过程,本篇在此基础上学习spring boot如何默认加载application.xml等文件信息的。

ConfigFileApplicationListener

在spring boot实战(第三篇)事件监听源码分析中可知在构造SpringApplication时加载相关的监听器,其中存在一个监听器ConfigFileApplicationListener,其定义如下:

[html] view plain copy

  1. public class ConfigFileApplicationListener implements
  2. ApplicationListener<ApplicationEvent>, Ordered {
  3. @Override
  4. public void onApplicationEvent(ApplicationEvent event) {
  5. if (event instanceof ApplicationEnvironmentPreparedEvent) {
  6. onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
  7. }
  8. if (event instanceof ApplicationPreparedEvent) {
  9. onApplicationPreparedEvent((ApplicationPreparedEvent) event);
  10. }
  11. }
  12. }

监听ApplicationEvent事件,在触发所有其子类以及本身事件时会执行其onApplicationEvent方法。在执行

[html] view plain copy

  1. for (SpringApplicationRunListener runListener : runListeners) {
  2. runListener.environmentPrepared(environment);
  3. }

时会触发到

[html] view plain copy

  1. if (event instanceof ApplicationEnvironmentPreparedEvent) {
  2. onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
  3. }

中;

[html] view plain copy

  1. private void onApplicationEnvironmentPreparedEvent(
  2. ApplicationEnvironmentPreparedEvent event) {
  3. Environment environment = event.getEnvironment();
  4. if (environment instanceof ConfigurableEnvironment) {
  5. onApplicationEnvironmentPreparedEvent((ConfigurableEnvironment) environment,
  6. event.getSpringApplication());
  7. }
  8. }

在上一篇中可以知道enviroment为StandardServletEnvironment实例,因此执行onApplicationEnvironmentPreparedEvent方法

[html] view plain copy

  1. private void onApplicationEnvironmentPreparedEvent(
  2. ConfigurableEnvironment environment, SpringApplication application) {
  3. addPropertySources(environment, application.getResourceLoader());
  4. bindToSpringApplication(environment, application);
  5. }

首先来看addPropertySources相关信息

[html] view plain copy

  1. protected void addPropertySources(ConfigurableEnvironment environment,
  2. ResourceLoader resourceLoader) {
  3. RandomValuePropertySource.addToEnvironment(environment);
  4. try {
  5. new Loader(environment, resourceLoader).load();
  6. }
  7. catch (IOException ex) {
  8. throw new IllegalStateException("Unable to load configuration files", ex);
  9. }
  10. }

RandomValuePropertySource.addToEnvironment(environment)将随机方法放入到PropertySources中

[html] view plain copy

  1. public static void addToEnvironment(ConfigurableEnvironment environment) {
  2. environment.getPropertySources().addAfter(
  3. StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
  4. new RandomValuePropertySource("random"));
  5. logger.trace("RandomValuePropertySource add to Environment");
  6. }

如何从Random中获取值是需要看getProperty方法:

[html] view plain copy

  1. public Object getProperty(String name) {
  2. if (!name.startsWith("random.")) {
  3. return null;
  4. }
  5. if (logger.isTraceEnabled()) {
  6. logger.trace("Generating random property for '" + name + "'");
  7. }
  8. if (name.endsWith("int")) {
  9. return getSource().nextInt();
  10. }
  11. if (name.startsWith("random.long")) {
  12. return getSource().nextLong();
  13. }
  14. if (name.startsWith("random.int") && name.length() > "random.int".length() + 1) {
  15. String range = name.substring("random.int".length() + 1);
  16. range = range.substring(0, range.length() - 1);
  17. return getNextInRange(range);
  18. }
  19. byte[] bytes = new byte[32];
  20. getSource().nextBytes(bytes);
  21. return DigestUtils.md5DigestAsHex(bytes);
  22. }

其中的getSource()表示Random类。

接下来看

[html] view plain copy

  1. new Loader(environment, resourceLoader).load()

看load方法

[html] view plain copy

  1. public void load() throws IOException {
  2. ...//处理profiles信息
  3. while (!this.profiles.isEmpty()) {
  4. String profile = this.profiles.poll();
  5. for (String location : getSearchLocations()) {
  6. if (!location.endsWith("/")) {
  7. // location is a filename already, so don't search for more
  8. // filenames
  9. load(location, null, profile);
  10. }
  11. else {
  12. for (String name : getSearchNames()) {
  13. load(location, name, profile);
  14. }
  15. }
  16. }
  17. }
  18. addConfigurationProperties(this.propertiesLoader.getPropertySources());
  19. }

看getSearchLocations()方法

[html] view plain copy

  1. private Set<String> getSearchLocations() {
  2. Set<String> locations = new LinkedHashSet<String>();
  3. // User-configured settings take precedence, so we do them first
  4. if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
  5. for (String path : asResolvedSet(
  6. this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) {
  7. if (!path.contains("$")) {
  8. if (!path.contains(":")) {
  9. path = "file:" + path;
  10. }
  11. path = StringUtils.cleanPath(path);
  12. }
  13. locations.add(path);
  14. }
  15. }
  16. locations.addAll(asResolvedSet(
  17. ConfigFileApplicationListener.this.searchLocations,
  18. DEFAULT_SEARCH_LOCATIONS));
  19. return locations;
  20. }

首先看CONFIG_LOCATION_PROPERTY(spring.config.location)是否存在配置,无则走默认配置路径DEFAULT_SEARCH_LOCATIONS(classpath:/,classpath:/config/,file:./,file:./config/)

继续来看getSearchNames()方法

[html] view plain copy

  1. private Set<String> getSearchNames() {
  2. if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
  3. return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
  4. null);
  5. }
  6. return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
  7. }

优先看CONFIG_NAME_PROPERTY(spring.config.name)配置,否则走DEFAULT_NAMES(application)

解析完路径和配置文件名以后,将开始判断路径+名称组合是否存在  执行load(...)方法

[html] view plain copy

  1. private void load(String location, String name, String profile)
  2. throws IOException {
  3. String group = "profile=" + (profile == null ? "" : profile);
  4. if (!StringUtils.hasText(name)) {
  5. // Try to load directly from the location
  6. loadIntoGroup(group, location, profile);
  7. }
  8. else {
  9. // Search for a file with the given name
  10. for (String ext : this.propertiesLoader.getAllFileExtensions()) {
  11. if (profile != null) {
  12. // Try the profile specific file
  13. loadIntoGroup(group, location + name + "-" + profile + "." + ext,
  14. null);
  15. // Sometimes people put "spring.profiles: dev" in
  16. // application-dev.yml (gh-340). Arguably we should try and error
  17. // out on that, but we can be kind and load it anyway.
  18. loadIntoGroup(group, location + name + "-" + profile + "." + ext,
  19. profile);
  20. }
  21. // Also try the profile specific section (if any) of the normal file
  22. loadIntoGroup(group, location + name + "." + ext, profile);
  23. }
  24. }
  25. }

this.propertiesLoader.getAllFileExtensions()方法获取文件后缀

[html] view plain copy

  1. public Set<String> getAllFileExtensions() {
  2. Set<String> fileExtensions = new HashSet<String>();
  3. for (PropertySourceLoader loader : this.loaders) {
  4. fileExtensions.addAll(Arrays.asList(loader.getFileExtensions()));
  5. }
  6. return fileExtensions;
  7. }

loader.getFileExtensions()获取所有支持的文件后缀,其中loader在执行load方法时实例化

[html] view plain copy

  1. public void load() throws IOException {
  2. this.propertiesLoader = new PropertySourcesLoader();
  3. ...}

调用其构造方法

[html] view plain copy

  1. public PropertySourcesLoader(MutablePropertySources propertySources) {
  2. Assert.notNull(propertySources, "PropertySources must not be null");
  3. this.propertySources = propertySources;
  4. this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
  5. null);
  6. }

可以看出this.loaders是由SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,null)得到

[html] view plain copy

  1. public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
  2. Assert.notNull(factoryClass, "'factoryClass' must not be null");
  3. ClassLoader classLoaderToUse = classLoader;
  4. if (classLoaderToUse == null) {
  5. classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  6. }
  7. List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
  8. if (logger.isTraceEnabled()) {
  9. logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
  10. }
  11. List<T> result = new ArrayList<T>(factoryNames.size());
  12. for (String factoryName : factoryNames) {
  13. result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
  14. }
  15. AnnotationAwareOrderComparator.sort(result);
  16. return result;
  17. }

加载META-INF/spring.factories文件下对应内容

[html] view plain copy

  1. # PropertySource Loaders
  2. org.springframework.boot.env.PropertySourceLoader=\
  3. org.springframework.boot.env.PropertiesPropertySourceLoader,\
  4. org.springframework.boot.env.YamlPropertySourceLoader

因此加载了PropertiesPropertySourceLoader以及YamlPropertySourceLoader类实例;

  • PropertiesPropertySourceLoader 支持文件后缀格式 "properties","xml"

[html] view plain copy

  1. @Override
  2. public String[] getFileExtensions() {
  3. return new String[] { "properties", "xml" };
  4. }
  • YamlPropertySourceLoader 支持文件后缀格式 "yml","yaml"

[html] view plain copy

  1. @Override
  2. public String[] getFileExtensions() {
  3. return new String[] { "yml", "yaml" };
  4. }

两者覆写的load方法实现如何处理资源为PropertySource对象。

获取完文件后缀后调用loadIntoGroup方法将资源信息转化为PropertySource,其实质为调用PropertySourcesLoader中load方法

[html] view plain copy

  1. private PropertySource<?> loadIntoGroup(String identifier, String location,
  2. String profile) throws IOException {
  3. Resource resource = this.resourceLoader.getResource(location);
  4. PropertySource<?> propertySource = null;
  5. if (resource != null) {
  6. String name = "applicationConfig: [" + location + "]";
  7. String group = "applicationConfig: [" + identifier + "]";
  8. propertySource = this.propertiesLoader.load(resource, group, name,
  9. profile);
  10. if (propertySource != null) {
  11. maybeActivateProfiles(propertySource
  12. .getProperty(ACTIVE_PROFILES_PROPERTY));
  13. addIncludeProfiles(propertySource
  14. .getProperty(INCLUDE_PROFILES_PROPERTY));
  15. }
  16. }
  17. StringBuilder msg = new StringBuilder();
  18. msg.append(propertySource == null ? "Skipped " : "Loaded ");
  19. msg.append("config file ");
  20. msg.append("'").append(location).append("'");
  21. if (StringUtils.hasLength(profile)) {
  22. msg.append(" for profile" + profile);
  23. }
  24. if (resource == null || !resource.exists()) {
  25. msg.append(" resource not found");
  26. }
  27. this.debug.add(msg);
  28. return propertySource;
  29. }

最后调用addConfigurationProperties(this.propertiesLoader.getPropertySources())方法将解析过后的资源信息放置进Enviroment中propertySources属性集合中

[html] view plain copy

  1. private void addConfigurationProperties(MutablePropertySources sources) {
  2. List<PropertySource<?>> reorderedSources = new ArrayList<PropertySource<?>>();
  3. for (PropertySource<?> item : sources) {
  4. reorderedSources.add(item);
  5. }
  6. // Maybe we should add before the DEFAULT_PROPERTIES if it exists?
  7. this.environment.getPropertySources().addLast(
  8. new ConfigurationPropertySources(reorderedSources));
  9. }

至此 application.xml等文件的加载分析结束。

时序图

简单的画了一下时序图,可能和实际调用存在出入,仅作参考使用

spring boot实战(第六篇)加载application资源文件源码分析相关推荐

  1. 图片加载框架Picasso - 源码分析

    简书:图片加载框架Picasso - 源码分析 前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的 ...

  2. 【Spring源码】ClassPathResource 资源文件源码分析

    上一篇文章我们主要介绍了开发 Spring 应用涉及到的一些核心组件,在文章的最后搭建了开发环境.那么接下来我们开始分析 Spring 源码部分,本篇文章首先分析 Spring 是如何封装资源文件的. ...

  3. os引导程序boot从扇区拷贝os加载程序loader文件到内存(boot copy kernel to mem in the same method)

    [0]README 0.1) 本代码旨在演示 在boot 代码中,如何 通过 loader文件所在根目录条目 找出该文件的 在 软盘所有全局扇区号(簇号),并执行内存中的 loader 代码: 0.2 ...

  4. android资源加载流程6,FrameWork源码解析(6)-AssetManager加载资源过程

    之前一段时间项目比较忙所以一直没有更新,接下来准备把插件化系列的文章写完,今天我们就先跳过ContentProvider源码解析来讲资源加载相关的知识,资源加载可以说是插件化非常重要的一环,我们很有必 ...

  5. Spark动态加载外部资源文件

    Spark动态加载外部资源文件 1.spark-submit --files 动态加载外部资源文件 之前做一个关于Spark的项目时,因项目中需要读取某个静态资源文件,然后在本地IDEA测试一切皆正常 ...

  6. OSG —— 笔记2 - 加载模型(附源码)

    效果         相关文章      OSG -- 笔记1 - 指令调用模型      OSG -- 笔记2 - 加载模型(附源码)      OSG -- 笔记3 - 绘制矩形(附源码)     ...

  7. 【java毕业设计】基于Spring Boot+mysql的酒店管理系统设计与实现(程序源码+毕业论文)-酒店管理系统

    基于Spring Boot+mysql的酒店管理系统设计与实现(程序源码+毕业论文) 大家好,今天给大家介绍基于Spring Boot+mysql的酒店管理系统设计与实现,本论文只截取部分文章重点,文 ...

  8. bytebuf池_Netty篇:ByteBuf之内存池源码分析

    Netty的内存池的实现有些复杂,使用了大量的位运算,晦涩难懂,不过万能的博客上好多大神已经介绍的非常详细,推荐四篇很详细很棒的源码分析的文章链接,本文根据自己的理解顺一下思路,内容主要也是出自以下四 ...

  9. 深入理解DOM事件类型系列第六篇——加载事件

    前面的话 提到加载事件,可能想到了window.onload,但实际上,加载事件是一大类事件,本文将详细介绍加载事件 load load事件是最常用的一个事件,当页面完全加载后(包括所有图像.java ...

最新文章

  1. C++语言学习(十二)——C++语言常见函数调用约定
  2. java中堆与栈的区别 彻底理解
  3. 基于python的游戏设计与实现-python五子棋游戏的设计与实现
  4. python处理pdf提取指定数据_python从PDF中提取数据的示例
  5. 大一java期末考笔试_大学java期末考试试题和答案
  6. React Router路由详解
  7. Qt工作笔记-QGraphicsView框架容易忽视的坐标问题-“画布”QgraphicsScene到底放在了哪儿?
  8. pycharm安装教程,超详细
  9. 力扣242.有效的字母异位词(JavaScript)
  10. 使用ExposedObject对Asp.net MVC中匿名类型的JsonResult做单元测试
  11. 新手学Docker(1)Hello World
  12. CSS3---3.相对父元素的伪类
  13. WPS简历模板的图标怎么修改_HR眼里的优秀简历模板长这样!30份中英文优秀模板,可一键修改!...
  14. Oracle11g Dataguard配置
  15. 新电脑如何进行磁盘分区?
  16. no accounts with itunes connect access问题排查解决
  17. python海龟库写名字
  18. 深入springboot怎么启动tomcat
  19. 基于深度学习对皮肤病进行识别设计与实现
  20. 猪是坚强的,所以取名朱坚强!

热门文章

  1. 限制域用户多点登录--脚本
  2. 《PowerShell V3——SQL Server 2012数据库自动化运维权威指南》——1.5 安装SMO
  3. 树莓派:VNC远程控制
  4. C#和Java在重写上的区别
  5. vim 使用技巧 转载
  6. Android配置环境问题
  7. Flask实战2问答平台-登录限制(装饰器)
  8. 格式说明_法律文书:公司单位民事起诉状格式范本及说明,最高人民法院2016...
  9. 【kernel 中内存分配那点事】
  10. 树莓派 系统的 启动与安装