优雅解决 SpringBoot 工程中多环境下 application.properties 的维护问题
微信号:geekoftaste, 期待与大家一起探讨!
背景
我们知道 SpringBoot 有一个全局的配置文件 application.properties, 可以把工程里用到的占位符,第三方库的配置项如 dubbo 端口,工程的 db 配置等统一放在这个配置文件里,方便对工程里所有配置项的统一管理。我们知道,在企业开发中,我们一般会先在测试环境中开发,在预发环境环境,最后上生产环境部署,也就意味着一个工程需要分别部署在测试,预发,生产环境上,而这三种环境的一些配置项(如测试环境和生产环境的 db 配置)很多时候都是不一样的,所以我们通常需要为每一个环境准备一份 application.properties, 接下来就引申出一个问题
如何维护多环境下的 application.properties 文件
方法一:在工程里维护多个环境的 application.properties ,部署的时候通过 spring.profiles.active 来指定工程应用哪个环境的 application.properties 文件,比如预发配置文件我们用 application-pre.properties, 线上配置文件我们用 application-prod.properties,当想在预发部署工程时,我们在部署脚本里用 java -jar xxxxx.jar --spring.profiles.active=pre 这样的方式来指定工程启动使用预发的 application-pre.properties 配置文件
以上这种方式虽然可以满足要求,但有一个比较棘手的问题:
在部署脚本里需要先根据 ip 等来判定当前环境是预发还是线上
if [ "$flag" == "预发" ]; then
java -jar xxxxx.jar --spring.profiles.active=pre
else
java -jar xxxxx.jar --spring.profiles.active=prod
如上所示,部署脚本需要根据一个标志来判断是预发还是线上,以便指定相应的 spring.profiles.active,这个标志的维护就是一个成本 ,每个环境的部署机器都要能正确设置这个 flag 的值,维护的成本很大
方法二:也就是我们工程当前采用的方式
除了用 spring.profiles.active 来指定到底用工程中哪种环境的 application.properties,我们还可以用
java -jar xxxxx.jar --spring.config.location=/opt/conf/application.properties
这种指定配置文件位置的方式来使用指定的 application.properties 文件
使用这种方式就解决了方法一的问题,只要在工程里维护多个环境下的 application.properties 文件(如下)
然后在部署时,当 gradle build 通过之后,在每个环境的机器部署前都会把环境对应的 application.properties 统一 copy 到机器的固定目录下,如 /opt/conf
下, 然后在启动的时候,通过在启动命令里指定 spring.config.location=/opt/conf/application.properties 的方式来指定 jar 包使用此环境下的 application.properties 文件即可
问题初现:多环境下的 application.properties 如何维护
在上图我们可以看到,由于我们有多个预发及线上环境,不得不为这些环境分别指定一个 application.properties,这样导致的后果就是如果我需要在一个 application.properties 文件里添加一个配置,不得不在其他环境下的 application.properties 文件里也 手动 添加此配置,这样的工作不仅烦琐,而且很容易出错, 之前就有发生过同事只在预发的 application.properties 加配置而忘记在线上加导致的线上部署失败的问题
如何解决
实际上预发和线上的配置大部分都是一样的,只有少部分是不一样的,所以我们想是否能将大部分一样的配置都统一放到一个文件(姑且叫 application-common.properties)里维护,这样如果要配置一个属性,只需要统一在这一个文件里配置即可,极大地降低了维护成本
那如何解决不同环境下某些配置不同的问题呢,比如在预发和线上我们对 MQ 的 topic 的命名有一个统一的规范,预发我们统一叫 topic-pre-xxx, 线上我们统一叫 topic-prod-xxx。
针对这种不同环境下配置不同的问题,我们可以单独为预发和线上建一个文件,比如预发叫 application-pre.properties, 线上叫 application-prod.properties,把各个环境独有的配置都分别放在这两个文件里即可
好了,现在我们有了三个文件,公用配置文件:application-common.properties, 以及两个环境配置文件:application-pre.properties, application-prod.properties,,那怎么生成如下最终各个环境的 application.properties 文件呢
很明显应该把公用配置文件与各个环境的配置文件合并
合并工作的思路很简单,我们以生成预发环境的 application.properties为例,如下
遍历 application-common.properties 文件中的每一行,然后取出每一行的 key,value(以等号分割),将其存储到 map中
遍历 application-pre.properties 的每一行,然后取出每一行的 key,value(以等号分割),取出的同时拿 key 到上一步的 map去查找, 如果存在则覆盖,如果不存在则在上一步的 map 中新增 key, value
遍历步骤 2 最终的 map将每个键值对以 key=value 的形式写入 pre 中的 applicaton.properties 文件
还有一个问题,这个合并工作写在哪里呢,答案是 gradle 的 task 里,我们的工程是基于 gradle 构建的,一般我们是通过 gradle build 来编译打包工程的,我们可以在 gradle build 打包之后再执行这个 task ,假设这个 task 名为 regeneratePropertyFile ,则可写成如下形式
build.finalize(regeneratePropertyFile)
最后我们来看看最终的合并生成各个环境 application.properties 的 task
task regeneratePropertyFile(type:Exec){
["pre", "prod"].each { env ->
def envMap = [:]
// 取出公共文件的键值对
def destFileName = "src/main/resources/META-INF/spring/properties/application-common.properties"
def destFile = file(destFileName)
String content = destFile.text
def commonLines = destFile.readLines()
for (line in commonLines) {
Integer equalSignLoc = line.indexOf("=")
if (equalSignLoc == -1) {
continue
}
String key = line.substring(0, equalSignLoc)
String value = line.substring(equalSignLoc + 1)
envMap.put(key, value)
}
// 取出环境配置文件 application.properties 的键值对,写入envMap中
File envFile = file("src/main/resources/META-INF/spring/properties/application-${env}.properties")
def envLines = envFile.readLines()
for (line in envLines) {
Integer equalSignLoc = line.indexOf("=")
if (equalSignLoc == -1) {
continue
}
String key = line.substring(0, equalSignLoc)
String value = line.substring(equalSignLoc + 1)
envMap.put(key, value)
}
content = ""
envMap.each { key, val ->
content = "${content}\n"
}
// 写入最终环境的 application.properties 文件
destFile = file("${rootProject.projectDir}/bb_conf/${env}/application.properties")
destFile.text = content
}
}
搞定!妈妈再也不用担心我在多环境下维护多个 application.properties 的噩梦了
公众号:「码农蜕变之路」,欢迎关注哦
优雅解决 SpringBoot 工程中多环境下 application.properties 的维护问题的更多相关文章
- 原 spring-boot工程中,jpa下hibernate的ddl-auto的各种属性
jpa: hibernate: ddl-auto: create ddl-auto:create----每次运行该程序,没有表格会新建表格,表内有数据会清空 ddl-auto:create-d ...
- 如何解决SpringBoot工程中的错误:java.sql.SQLNonTransientConnectionException: CLIENT_PLUGIN_AUTH is required
出错原因:MySQL依赖及MySQL驱动包高于在使用的MySQL数据库版本. 比如,我本地数据库版本是:Server version: 5.2.3-falcon-alpha-community-nt ...
- 全世界最详细的图形化VMware中linux环境下oracle安装(二)【weber出品必属精品】
<ORACLE 10.2.05版本的升级补丁安装> 首先我们解压 $ unzip p8202632_10205_LINUX.zip 解压后我们会发现多出了个文件夹,他是:Disk1,进入D ...
- 全世界最详细的图形化VMware中linux环境下oracle安装(一)【weber出品必属精品】
安装流程:前期准备工作--->安装ORACLE软件--->安装升级补丁--->安装odbc创建数据库--->安装监听器--->安装EM <前期准备工作> 安装 ...
- codec engine工程中使用ccs下编译的lib库
原文地址:codec engine工程中使用ccs下编译的lib库--转作者:木子小白 这两天将dsp的算法程序放到ccs下,生成lib库文件 这样的好处就是: 1. 算法封装成lib库以后,看不到源 ...
- springboot+eureka+mybatis+mysql环境下报504 Gateway Time-out
1.test环境下的数据库配置的 driver和url有问题, 在工程日志中的表现是不能打印出最新的日志,在部署前的日志能看到报错:
- 在Windows中单机环境下创建RabbitMQ集群
本文根据:http://www.360doc.com/content/15/0312/17/20874412_454622619.shtml整理而来 RabbitMQ具有很好的消息传递性能,同时又是开 ...
- 二维码识别:Halcon与C++中多字节环境下的字节编码格式设置和转换
Halcon环境下可通过设置set_system(‘filename_encoding’, ‘utf8’),可以将二维码的识别结果解析出汉字. VS环境下则需要将utf8转换成gbk格式.代码如下: ...
- 解决springboot项目中@Value注解参数值为null的问题
1.错误场景: springboot项目中在.properties文件(.yml)文件中配置了属性值,在Bean中使用@Value注解引入该属性,Bean的构造器中使用该属性进行初始化,此时有可能会出 ...
随机推荐
- Pandas 计算工具介绍
# 导入相关库 import numpy as np import pandas as pd 统计函数 最常见的计算工具莫过于一些统计函数了.首先构建一个包含了用户年龄与收入的 DataFrame i ...
- php sublime常用插件
php sublime常用插件 1 Sublime Text的默认设置是不开启显示编码的,如果想开启,可通过菜单Perference → Settings – User,在打开的配置文件里 ,在大括号 ...
- jquery mobiscroll移动端日期选择控件(无乱码)
jquery mobiscroll移动端日期选择控件(无乱码) <pre><!DOCTYPE html><html lang="en">< ...
- c#数据结构之Array、ArrayList、List、LinkedList对比分析
一.前言: 在c#数据结构中,集合的应用非常广泛,无论是做BS架构还是CS架构开发,都离不开集合的使用,比如我们常见的集合包括:Array.ArrayList.List.LinkedList等.这一些 ...
- Spring 框架常用语法进行总结
Spring 框架常用语法进行总结: spring框架的二大主要的功能就是IOC和AOP. IOC: 控制反转(依赖注入) AOP: 面向切面编程 学习spring最好的方法就是去看官网,里面有详细的 ...
- 打包上传被拒 Guideline 2.5.1 - Performance - Software Requirements
打包上传被拒 Guideline 2.5.1 - Performance - Software Requirements 在项目中全部搜索:prefs:root 找到后,把这个私有的 NSURL *u ...
- mysql查询不重复的行内容,不重复的记录数.count,distinct
有这么一个表 记录了id, p_id, p_name , p_content , p_time 1 343 aaa aaaaaa 2012-09-01 2 ...
- 2019年PHP面试题附答案(实战经验)
出于一些原因近期做了一次工作变动,在职交接近一个半月时间大概面试了十五家公司,并且得到了自己比较满意的offer,最后基本上无缝衔接了新工作.总体来说,虽然准备的很充分,但面试期间还是暴露了许多问题, ...
- 领扣(LeetCode)删除注释 个人题解
给一个 C++ 程序,删除程序中的注释.这个程序source是一个数组,其中source[i]表示第i行源码. 这表示每行源码由\n分隔. 在 C++ 中有两种注释风格,行内注释和块注释. 字符串// ...
- bootstrap中图片响应式
主要解决的是在轮播图中图片响应式的问题 目的 各种终端都需要正常显示图片 移动端应该使用更小(体积)的图片 实现方式 给标签添加两个data-属性(如:data-img-sm="小图路径&q ...