热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

SpringBoot加载配置文件的完整步骤

这篇文章主要给大家介绍了关于SpringBoot加载配置文件的完整步骤,文中通过示例代码介绍的非常详细,对大家的学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

本文针对版本2.2.0.RELEASE来分析SpringBoot的配置处理源码,通过查看SpringBoot的源码来弄清楚一些常见的问题比如:

  1. SpringBoot从哪里开始加载配置文件?
  2. SpringBoot从哪些地方加载配置文件?
  3. SpringBoot是如何支持yaml和properties类型的配置文件?
  4. 如果要支持json配置应该如何做?
  5. SpringBoot的配置优先级是怎么样的?
  6. placeholder是如何被解析的?

带着我们的问题一起去看一下SpringBoot配置相关的源代码,找出问题的答案。

SpringBoot从哪里开始加载配置文件?

SpringBoot加载配置文件的入口是由ApplicationEnvironmentPreparedEvent事件进入的,SpringBoot会在SpringApplication的构造函数中通过spring.factories文件获取ApplicationListener的实例类:

public SpringApplication(ResourceLoader resourceLoader, Class<&#63;>... primarySources) {
 ...
 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
 ...
}

spring.factories中有一个ConfigFileApplicationListener类,它会监听ApplicationEnvironmentPreparedEvent然后再加载配置文件 :

# Application Listeners
org.springframework.context.ApplicatiOnListener= org.springframework.boot.context.config.ConfigFileApplicationListener
...

有了事件和事件处理的类后,再找出发送事件的地方,就可以搞清楚SpringBoot是怎么加载配置文件的了,SpringBoot在启动之前先初始化好SpringApplicationRunListeners这个类,它会实现SpringApplicationRunListener接口然后对事件进行转发:

class SpringApplicationRunListeners {

 private final Log log;

 private final List listeners;

 SpringApplicationRunListeners(Log log, Collection<&#63; extends SpringApplicationRunListener> listeners) {
 this.log = log;
 this.listeners = new ArrayList<>(listeners);
 }
 
 void environmentPrepared(ConfigurableEnvironment environment) {
 for (SpringApplicationRunListener listener : this.listeners) {
 listener.environmentPrepared(environment);
 }
 }
 ...
}

获取SpringApplicationRunListeners的代码如下:

private SpringApplicationRunListeners getRunListeners(String[] args) {
 Class<&#63;>[] types = new Class<&#63;>[] { SpringApplication.class, String[].class };
 return new SpringApplicationRunListeners(logger,
 getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

同样也会去加载spring.factories文件,该文件有一个EventPublishingRunListener类,该类的作用就是SpringBoot的事件转换成ApplicationEvent发送出去。

# Run Listeners
org.springframework.boot.SpringApplicatiOnRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

小结

  • SpringBoot会将事件转换成ApplicationEvent再分发
  • SpringBoot是通过监听ApplicationEnvironmentPreparedEvent事件来加载配置文件的
  • ConfigFileApplicationListener是处理配置文件的主要类

SpringBoot从哪些地方加载配置文件?

上面已经分析到ConfigFileApplicationListener是处理配置文件的主要类,然后进一步的查看SpringBoot是从哪些地址加载配置文件,进入ConfigFileApplicationListener类后会有两个默认的常量:

private static final String DEFAULT_SEARCH_LOCATIOnS= "classpath:/,classpath:/config/,file:./,file:./config/";
private static final String DEFAULT_NAMES = "application";

首先在没有任何配置的情况下,会从DEFAULT_SEARCH_LOCATIONS常量列出来的位置中加载文件名为DEFAULT_NAMES(.properties或yml)的文件,默认位置包括:

  • classpath根目录(classpath:/)
  • classpath里面的config文件目录(classpath:/config/)
  • 程序运行目录(file:./)
  • 程序运行目录下的config目录(file:./config/)

上面说的是没有额外配置的情况,SpringBoot足够灵活可以指定配置文件搜索路径、配置文件名,在ConfigFileApplicationListener类中有个getSearchLocations方法,它主要负责获取配置搜索目录:

private Set getSearchLocations() {
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
 return getSearchLocations(CONFIG_LOCATION_PROPERTY);
 }
 Set locatiOns= getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
 locations.addAll(
 asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
 return locations;
}

它的操作步骤大致如下:

  1. 检查是否有spring.config.location属性,如果存在则直接使用它的值
  2. 从spring.config.additional-location属性中获取搜索路径
  3. 将默认搜索路径添加到搜索集合

这里就可以确定SpringBoot配置的搜索路径有两种情况:如果配置了spring.config.location则直接使用,否则使用spring.config.additional-location的属性值 + 默认搜索路径。

SpringBoot是如何支持yaml和properties类型的配置文件?

SpringBoot的配置支持properties和yaml文件,SpringBoot是如何解析这两种文件的呢,继续分析ConfigFileApplicationListener这个类,里面有个子类叫Loader加载配置文件主要的工作就是由这货负责,但是直接读取properties和yaml并转换成PropertySource还是由里面的PropertySourceLoader负责:

Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
 ...
 this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
 getClass().getClassLoader());
}

构造Loader对象的时候就会先加载PropertySourceLoader,加载方式还是从spring.factories中读取:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

其中配置了两个PropertySourceLoader的实现类:

  • PropertiesPropertySourceLoader
  • YamlPropertySourceLoader

看名字就知道是分别负责properties和yaml的啦。

如果要支持json配置应该如何做?

如果不喜欢properties和yaml这两种格式,想要定义json做为配置文字格式可以直接定义json类型的PropertySourceLoader:

public class JSONPropertySourceLoader implements PropertySourceLoader {

 @Override
 public String[] getFileExtensions() {
 return new String[] {"json"};
 }
 
 @Override
 public List> load(String name, Resource resource) throws IOException {

 if(resource == null || !resource.exists()){
  return Collections.emptyList();
 }

 Map cOnfigs= JSON.parseObject(resource.getInputStream(), Map.class);

 return Collections.singletonList(
  new MapPropertySource(name, configs)
 );
 }
}

然后在resources目录里面建立个META-INF,再添加个spring.factories里面的内容如下:

org.springframework.boot.env.PropertySourceLoader=\
com.csbaic.arch.spring.env.loader.JSONPropertySourceLoader

最后在resources目录里面建个application.json的配置文件 :

{
 "spring.application.name": "JSONConfig"
}

正常启动SpringBoot获取spring.applicaiton.name的配置的值就是JSONConfig:

2019-11-02 14:50:17.730  INFO 55275 --- [           main] c.c.a.spring.env.SpringEnvApplication    : JSONConfig

SpringBoot的配置优先级是怎么样的?

SpringBoot中有个PropertySource接口,专门用来保存属性常见的实现类有:

  • CommandLinePropertySource
  • MapPropertySource
  • SystemEnvironmentPropertySource
  • ....

另外为了集中管理PropertySource还抽象出一个PropertySources接口,PropertySources就一个实现类叫:MutablePropertySources,它将所有的PropertySource都放置在一个名叫propertySourceList集合中,同时提供一些修改操作方法:

public void addFirst(PropertySource<&#63;> propertySource) {}
public void addLast(PropertySource<&#63;> propertySource) {}
public void addBefore(String relativePropertySourceName, PropertySource<&#63;> propertySource) {}
public void addAfter(String relativePropertySourceName, PropertySource<&#63;> propertySource) {}
public int precedenceOf(PropertySource<&#63;> propertySource) { }
public PropertySource<&#63;> remove(String name) {}
public void replace(String name, PropertySource<&#63;> propertySource) {}

所有的PropertySource都保存在propertySourceList中,越小的索引优先级越高,所以如果想要覆盖属性只要保证优化级够高就行。

placeholder是如何被解析的&#63;

继续分析ConfigFileApplicationListener的Loader子类,在构造时还会创建一个PropertySourcesPlaceholdersResolver,placeholder的解析都由它来完成:

Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {

 this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
}

分析PropertySourcesPlaceholdersResolver发现,真正完成解析是由PropertyPlaceholderHelper完成,PropertySourcesPlaceholdersResolver 在构造的时候就会创建一个PropertyPlaceholderHelper

public PropertySourcesPlaceholdersResolver(Iterable> sources, PropertyPlaceholderHelper helper) {
 this.sources = sources;
 this.helper = (helper != null) &#63; helper : new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
  SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true);
}

PropertySourcesPlaceholdersResolver 在创建 PropertyPlaceholderHelper 的时候会传递三个参数:前缀、后缀、默认值分割符,分别由以下三个常量表示:

public static final String PLACEHOLDER_PREFIX = "${";
public static final String PLACEHOLDER_SUFFIX = "}";
public static final String VALUE_SEPARATOR = ":";

这样 PropertyPlaceholderHelper 在解析placeholder时就能知道以什么格式来解析比如:${spring.application.name}这个placeholder就会被解析成属性值。

总结

SpringBoot的配置非常灵活配置可以来自文件、环境变量、JVM系统属性、配置中心等等,SpringBoot通过
PropertySource和PropertySources实现属性优先级、CRUD的统一管理,为开发者提供统一的配置抽象。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。


推荐阅读
  • 本文详细介绍了Ionic框架的使用方法及其与Angular的集成。Ionic框架是一个强大的前端开发工具,适用于构建跨平台的移动应用程序。文章将探讨如何引入必要的CSS和JavaScript文件,并解释bundle.js中包含的核心功能,如路由等。 ... [详细]
  • 自 Node.js 6.3 版本起,调试功能已内置在核心模块中,无需额外安装 node-inspector 等工具。通过简单的命令即可启动调试模式,并利用 Chrome 浏览器进行高效的代码调试。 ... [详细]
  • 本文介绍如何使用 Angular 6 的 HttpClient 模块来获取 HTTP 响应头,包括代码示例和常见问题的解决方案。 ... [详细]
  • 基于Node.js、Express、MongoDB和Socket.io的实时聊天应用开发
    本文详细介绍了使用Node.js、Express、MongoDB和Socket.io构建的实时聊天应用程序。涵盖项目结构、技术栈选择及关键依赖项的配置。 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • Java项目分层架构设计与实践
    本文探讨了Java项目中应用分层的最佳实践,不仅介绍了常见的三层架构(Controller、Service、DAO),还深入分析了各层的职责划分及优化建议。通过合理的分层设计,可以提高代码的可维护性、扩展性和团队协作效率。 ... [详细]
  • 本文介绍如何从JSON格式的文件中提取数据并将其分配给Bash脚本中的变量。我们将探讨具体的命令和工具,帮助你高效地完成这一任务。 ... [详细]
  • 在PHP后端开发中遇到一个难题:通过第三方类文件发送短信功能返回的JSON字符串无法解析。本文将探讨可能的原因并提供解决方案。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • 本文探讨了如何在 Pug 模板中正确地使用 JSON 插值,并解决了相关文档不足的问题。我们将介绍通过 gulp-pug 处理 JSON 数据的具体方法,以及如何在模板中插入和显示这些数据。 ... [详细]
  • 深入理解ExtJS:从入门到精通
    本文详细介绍了ExtJS的功能及其在大型企业前端开发中的应用。通过实例和详细的文件结构解析,帮助初学者快速掌握ExtJS的核心概念,并提供实用技巧和最佳实践。 ... [详细]
  • Python + Pytest 接口自动化测试中 Token 关联登录的实现方法
    本文将深入探讨 Python 和 Pytest 在接口自动化测试中如何实现 Token 关联登录,内容详尽、逻辑清晰,旨在帮助读者掌握这一关键技能。 ... [详细]
  • ElasticSearch 集群监控与优化
    本文详细介绍了如何有效地监控 ElasticSearch 集群,涵盖了关键性能指标、集群健康状况、统计信息以及内存和垃圾回收的监控方法。 ... [详细]
  • MongoDB的核心特性与架构解析
    本文深入探讨了MongoDB的核心特性,包括其强大的查询语言、灵活的文档模型以及高效的索引机制。此外,还详细介绍了MongoDB的体系结构,解释了其文档、集合和数据库的层次关系,并对比了MongoDB与传统关系型数据库(如MySQL)的逻辑结构。 ... [详细]
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
author-avatar
我的小名-_164
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有