点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者 | 二师兄

来源 | 程序新视界

在实践的过程中我们经常会遇到不同的环境需要不同配置文件的情况,如果每换一个环境重新修改配置文件或重新打包一次会比较麻烦,Spring Boot为此提供了Profile配置来解决此问题。

Profile的作用

Profile对应中文并没有合适的翻译,它的主要作用就是让Spring Boot可以根据不同环境提供不同的配置功能支持。

我们经常遇到这样的场景:有开发、测试、生产等环境,不同的环境又有不同的配置。如果每个环境在部署时都需要修改配置文件将会非常麻烦,而通过Profile则可以轻松解决改问题。

Profile的基本使用

比如上述环境,我们可以在Spring Boot中创建4个文件:

  • applcation.properties:公共配置

  • application-dev.properties:开发环境配置

  • application-test.properties:测试环境配置

  • application-prod.properties:生产环境配置

在applcation.properties中配置公共配置,然后通过如下配置激活指定环境的配置:

spring.profiles.active = prod

其中“prod”对照文件名中application-prod.properties。Spring Boot在处理时会获取配置文件applcation.properties,然后通过指定的profile的值“prod”进行拼接,获得application-prod.properties文件的名称和路径。

举例说明,比如在开发环境使用服务的端口为8080,而在生产环境中需要使用18080端口。那么,在application-prod.properties中配置如下:

server.port=8080

而在application-prod.properties中配置为:

server.port=18080

在使用不同环境时,可以通过在applcation.properties中配置spring.profiles.active属性值来进行指定(如上面示例),也可以通过启动命令参数进行指定:

java -jar springboot.jar --spring.profiles.active=prod

这样打包后的程序只需通过命令行参数就可以使用不同环境的配置文件。

基于yml文件类型

如果配置文件是基于yml文件类型,还可以将所有的配置放在同一个配置文件中:

spring:
profiles:
active: prodserver:
port: 18080---
spring:
profiles: devserver:
port: 8080---
spring:
profiles: testserver:
port: 8081

上述配置以“---”进行分割,其中第一个位置的为默认的profile,比如上述配置启动时,默认使用18080端口。如果想使用指定的端口同样可以采用上述命令启动时指定。

源码解析

下面带大家简单看一下在Spring Boot中针对Profile的基本处理流程(不会过度细化具体操作)。在Spring Boot启动的过程中执行其run方法时,会执行如下一段代码:

public ConfigurableApplicationContext run(String... args) {// ...try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// ...} // ...
}

其中prepareEnvironment方法的相关代码如下:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// ...listeners.environmentPrepared(environment);// ...
}

在prepareEnvironment方法处理业务的过程中会调用SpringApplicationRunListeners的environmentPrepared方法发布事件。该方法会遍历注册在spring.factories中SpringApplicationRunListener实现类,然后调用其environmentPrepared方法。

其中spring.factories中SpringApplicationRunListener实现类注册为:

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

SpringApplicationRunListeners方法中的调用代码如下:

void environmentPrepared(ConfigurableEnvironment environment) {for (SpringApplicationRunListener listener : this.listeners) {listener.environmentPrepared(environment);}
}

其中listeners便是注册的类的集合,这里默认只有EventPublishingRunListener。它的environmentPrepared方法实现为:

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

其实就是发布了一个监听事件。该事件会被同样注册在spring.factories中的ConfigFileApplicationListener监听到:

# Application Listeners
org.springframework.context.ApplicationListener=\
// ...
org.springframework.boot.context.config.ConfigFileApplicationListener
// ...

关于配置文件的核心处理便在ConfigFileApplicationListener中完成。在该类中我们可以看到很多熟悉的常量:

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {private static final String DEFAULT_PROPERTIES = "defaultProperties";// Note the order is from least to most specific (last one wins)private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";private static final String DEFAULT_NAMES = "application";// ...
}

比如Spring Boot默认寻找的配置文件的名称、默认扫描的类路径等。

不仅如此,该类还实现了监听器SmartApplicationListener接口和EnvironmentPostProcessor接口也就是拥有了监听器和环境处理的功能。

其onApplicationEvent方法对接收到事件进行判断,如果是ApplicationEnvironmentPreparedEvent事件则调用onApplicationEnvironmentPreparedEvent方法进行处理,代码如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);}if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent(event);}
}

onApplicationEnvironmentPreparedEvent会获取到对应的EnvironmentPostProcessor并调用其postProcessEnvironment方法进行处理。而loadPostProcessors方法获取的EnvironmentPostProcessor正是在spring.factories中配置的当前类。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();postProcessors.add(this);AnnotationAwareOrderComparator.sort(postProcessors);for (EnvironmentPostProcessor postProcessor : postProcessors) {postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());}
}

经过一系列的调用,最终调用到该类的如下方法:

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {RandomValuePropertySource.addToEnvironment(environment);new Loader(environment, resourceLoader).load();
}

其中Loader类为ConfigFileApplicationListener内部类,提供了具体处理配置文件优先级、profile、加载解析等功能。

比如,在Loader类的load方法中便有如下一段代码:

for (PropertySourceLoader loader : this.propertySourceLoaders) {if (canLoadFileExtension(loader, location)) {load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);return;}
}

该代码遍历PropertySourceLoader列表,并进行对应配置文件的解析,而这里的列表中的PropertySourceLoader同样配置在spring.factories中:

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

查看这两PropertySourceLoader的源代码,会发现SpringBoot默认支持的配置文件格式及解析方法。

public class PropertiesPropertySourceLoader implements PropertySourceLoader {private static final String XML_FILE_EXTENSION = ".xml";@Overridepublic String[] getFileExtensions() {return new String[] { "properties", "xml" };}@Overridepublic List<PropertySource<?>> load(String name, Resource resource) throws IOException {Map<String, ?> properties = loadProperties(resource);// ...}@SuppressWarnings({ "unchecked", "rawtypes" })private Map<String, ?> loadProperties(Resource resource) throws IOException {String filename = resource.getFilename();if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {return (Map) PropertiesLoaderUtils.loadProperties(resource);}return new OriginTrackedPropertiesLoader(resource).load();}
}

比如PropertiesPropertySourceLoader中支持了xml和properties两种格式的配置文件,并分别提供了PropertiesLoaderUtils和OriginTrackedPropertiesLoader两个类进行相应的处理。

同样的YamlPropertySourceLoader支持yml和yaml格式的配置文件,并且采用OriginTrackedYamlLoader类进行解析。

public class YamlPropertySourceLoader implements PropertySourceLoader {@Overridepublic String[] getFileExtensions() {return new String[] { "yml", "yaml" };}@Overridepublic List<PropertySource<?>> load(String name, Resource resource) throws IOException {// ...List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();// ...}
}

当然,在ConfigFileApplicationListener类中还实现了上面提到的如何拼接默认配置文件和profile的实现,相关代码如下:

private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {// ...if (profile != null) {String profileSpecificFile = prefix + "-" + profile + fileExtension;load(loader, profileSpecificFile, profile, defaultFilter, consumer);load(loader, profileSpecificFile, profile, profileFilter, consumer);// Try profile specific ps in files we've already processedfor (Profile processedProfile : this.processedProfiles) {if (processedProfile != null) {String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;load(loader, previouslyLoaded, profile, profileFilter, consumer);}}}// ...
}

ConfigFileApplicationListener类中还实现了其他更多的功能,大家感兴趣的话可以debug进行阅读。

本文通过OpenWrite的Markdown转换工具发布

关注我,回复“加群”加入各种主题讨论群

  • 文言文已经没啥用了?错!还能编程用!

  • Spring Cloud 2020 年路线图

  • REST API 的安全基础

  • 一条简单的更新语句,MySQL是如何加锁的?

  • IntelliJ 平台 2020 年路线图

点击“阅读原文免费领取价值199元学习大礼包

Spring Boot Profile使用详解及配置源码解析相关推荐

  1. Spring Cloud限流详解(附源码)

    在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud中如何实现限流. 在 Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法. ...

  2. Spring Cloud限流详解(内含源码)

    为什么80%的码农都做不了架构师?>>>    原文:http://www.itmuch.com/spring-cloud-sum/spring-cloud-ratelimit/ 在 ...

  3. SpringBoot2.1.5(16)--- Spring Boot的日志详解

    SpringBoot2.1.5(16)--- Spring Boot的日志详解 市面上有许多的日志框架,比如 JUL( java.util.logging), JCL( Apache Commons ...

  4. Spring Boot 集成 FreeMarker 详解案例

    年轻就不应该让自己过得太舒服" – From yong 一.Springboot 那些事 SpringBoot 很方便的集成 FreeMarker ,DAO 数据库操作层依旧用的是 Myba ...

  5. 8145v5 参数_SpringBoot外化配置源码解析:外化配置简介、参数处理|value|spring|调用|参数值

    SpringBoot外化配置源码解析 在前面章节我们讲解了 Spring Boot 的运作核心原理及启动过程中进行的一系列核心操作. 从本章开始,我们将针对在实践过程中应用的不同知识点的源代码进行解读 ...

  6. JAVA文件上传详解(附源码)

    文章目录 JAVA文件上传详解(附源码) 1.准备工作 2.使用类介绍 FileItem类 ServletFileUpload类 3.代码编写 JAVA文件上传详解(附源码) 在web应用中,文件上传 ...

  7. android WebView详解,常见漏洞详解和安全源码(下)

    上篇博客主要分析了 WebView 的详细使用,这篇来分析 WebView 的常见漏洞和使用的坑.  上篇:android WebView详解,常见漏洞详解和安全源码(上)  转载请注明出处:http ...

  8. android WebView详解,常见漏洞详解和安全源码(上)

    这篇博客主要来介绍 WebView 的相关使用方法,常见的几个漏洞,开发中可能遇到的坑和最后解决相应漏洞的源码,以及针对该源码的解析.  由于博客内容长度,这次将分为上下两篇,上篇详解 WebView ...

  9. FPGA学习之路—接口(3)—SPI详解及Verilog源码分析

    FPGA学习之路--SPI详解及Verilog源码分析 概述 SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线. 优点 支持全 ...

最新文章

  1. 谁干的mysql无密码登录?
  2. HDLBits 系列(41)根据仿真波形来设计电路之组合逻辑
  3. Mysql Mariadb 创建新用户
  4. Golang入门(1):安装与配置环境变量的意义
  5. java 日期for循环_java for循环的时候增加循环体的长度是不是不太好的?
  6. 简练软考知识点整理-互联网+
  7. python是什么类型的编程语言-python是一种什么类型的编程语言
  8. 接收诊断响应的相关CAPL函数,具有较高的可复用性
  9. CPU的DataSheet和UserManual的区别
  10. 微信小程序轮播图点击跳转页面
  11. 运维工程师与php,php新手入门的基础内容①
  12. Springboot电子病历管理APP毕业设计源码010350
  13. 洛谷P1425小鱼的游泳时间【c语言】
  14. 计算机的储存器包括内存储器和外存储器,内存储器和外存储器的分类与区别
  15. alpine的介绍与使用
  16. cesium实用功能-选中gltf、glb拖动修改位置,bim没试
  17. Python与金融:为什么将Python用于金融
  18. user account control用户帐户控制
  19. POJ1036 Gangsters
  20. 功能:输入一个正整数,按照从小到大的顺序输出它的所有质因子(重复的也要列举)(如180的质因子为2 2 3 3 5 )

热门文章

  1. linux tar包 追加
  2. linux shell 使用代理 proxychains 简介
  3. linux 文件 目录 默认权限
  4. python3 if else 简洁写法 三元运行
  5. linux 内核 调试工具介绍
  6. 原始套接字SOCK_RAW
  7. 国际化困境(第二篇)
  8. Android开发--图形图像与动画(四)--AnimationListener简介
  9. 查linux还是unix,C、C++判断操作系统是Linux、windows还是Unix
  10. Java登陆页面经常出现的问题,问一下关于登陆页面的有关问题