SpringBoot Profile使用详解及配置源码解析
在实践的过程中我们经常会遇到不同的环境需要不同配置文件的情况,如果每换一个环境重新修改配置文件或重新打包一次会比较麻烦,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: prod
server:
port: 18080
---
spring:
profiles: dev
server:
port: 8080
---
spring:
profiles: test
server:
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";
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
@Override
public 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 {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public 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 sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// ...
}
ConfigFileApplicationListener类中还实现了其他更多的功能,大家感兴趣的话可以debug进行阅读。
原文链接:《SpringBoot Profile使用详解及配置源码解析》
Spring技术视频
CSDN学院:《Spring Boot 视频教程全家桶》
程序新视界:精彩和成长都不容错过
![程序新视界-微信公众号](https://img2018.cnblogs.com/blog/1742867/201910/1742867-20191013111755842-2090947098.png)
SpringBoot Profile使用详解及配置源码解析的更多相关文章
- 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING
<Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th 2014 Email:skyseraph00@163.com 更多精彩请直接 ...
- Android中Canvas绘图基础详解(附源码下载) (转)
Android中Canvas绘图基础详解(附源码下载) 原文链接 http://blog.csdn.net/iispring/article/details/49770651 AndroidCa ...
- Android事件传递机制详解及最新源码分析——ViewGroup篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...
- 【详解】ThreadPoolExecutor源码阅读(三)
系列目录 [详解]ThreadPoolExecutor源码阅读(一) [详解]ThreadPoolExecutor源码阅读(二) [详解]ThreadPoolExecutor源码阅读(三) 线程数量的 ...
- 【详解】ThreadPoolExecutor源码阅读(二)
系列目录 [详解]ThreadPoolExecutor源码阅读(一) [详解]ThreadPoolExecutor源码阅读(二) [详解]ThreadPoolExecutor源码阅读(三) AQS在W ...
- 【详解】ThreadPoolExecutor源码阅读(一)
系列目录 [详解]ThreadPoolExecutor源码阅读(一) [详解]ThreadPoolExecutor源码阅读(二) [详解]ThreadPoolExecutor源码阅读(三) 工作原理简 ...
- Android 网络框架之Retrofit2使用详解及从源码中解析原理
就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...
- tp6源码解析-第二天,ThinkPHP6编译模板流程详解,ThinkPHP6模板源码详解
TP6源码解析,ThinkPHP6模板编译流程详解 前言:刚开始写博客.如果觉得本篇文章对您有所帮助.点个赞再走也不迟 模板编译流程,大概是: 先获取到View类实例(依赖注入也好,通过助手函数也好) ...
- Android事件传递机制详解及最新源码分析——Activity篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在前两篇我们共同探讨了事件传递机制<View篇>与<ViewGroup篇>,我们知道View触摸事件是ViewGroup传递 ...
随机推荐
- Mysql开启慢查询命令
; 查询: SHOW VARIABLES LIKE '%slow_query_log%'; 结果:
- jQuery相关方法7----各种事件和绑定事件
一.jQuery事件 1.鼠标事件 click与dbclick事件 click事件其实是由mousedown与mouseup 2个动作构成,所以点击的动作只有在松手后才触发 $ele.click(): ...
- 「CF150E」Freezing with Style「点分治」「单调队列」
题意 给定一颗带边权的树,求一条边数在\(L\).\(R\)之间的路径,并使得路径上边权的中位数最大.输出一条可行路径的两个端点.这里若有偶数个数,中位数为中间靠右的那个. \(n, L, R\leq ...
- word 实现多级列表编号
- mysql 修改表结构以支持事务操作
修改表的类型为 INNODB 的 SQL: alter table category_ ENGINE = innodb; 查看表的类型的 SQL show table status from ...
- SSO单点登录实例
单点登录流程图 系统登陆拦截器 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernf ...
- Linux下java环境变量配置
安装步骤 1.查看当前Linux系统是否安装java rpm -qa | grep java 2.卸载系统中已经存在的openJDK rpm -e --nodeps java--openjdk-1.7 ...
- 5分钟学会如何创建spring boot项目
上一篇博客说了如何创建spring boot项目,但是有些同学会觉得有点麻烦,有没有什么快速学会能快速创建spring boot项目的方法,答案是肯定的.接下来我们就一起来快速创建一个spring b ...
- 十一月百度杯pwnme 详细wp
目录 程序基本信息 程序溢出点 整体思路 exp脚本 成功获得flag 参考 程序基本信息 可以看到开启了栈不可执行和got表不可写保护. 程序溢出点 在函数sub_400AF7中,v8可以读入0x1 ...
- 关于linux下的系统存储管理
https://blog.csdn.net/aaaaaab_/article/details/80159871 //查看当前系统磁盘使用空间 df -h //查看当前目录文件占用空间大小 du -sh ...