这是我参与8月更文挑战的第12天,活动详情查看: 8月更文挑战
遇到的问题
首先在spring cloud微服务化的开发大环境下,配置的动态化必不可少,spring cloud 自身集成的config 配置中心就可以实现但是项目遇到了问题很不舒服: 我们公司spring cloud 集成config 方案就是一个bootstrap.yml文件作为加载启动文件,所有的配置从配置中心加载
server:
port: 8688
#测试环境
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8601/eureka/
instance:
prefer-ip-address: true
spring:
application:
name: cccc
cloud:
config:
discovery:
enabled: true
service-id: config
profile: dev,redis
label: master
大致就是上面那样,那么如果我要部署本地环境和线上环境呢,就是修改注册中心地址,根据分支进行控制
这样的话线上部署就用prod分支,本地部署就用local分支,测试部署就用test,但是这样很容易出现问题,就是改完代码之后分支的合并得小心,不能合并配置文件,否则会出现配置出错的问题
传统的 spring boot项目 其实可以根据配置文件的名字进行环境的切换
这样需要什么环境根据jvm参数或者maven打包参数进行构建就行
这个问题找了好久其实可以有这几种方案
指定booststrap.yml的文件位置,通过 --spring.cloud.bootstrap.location= 启动参数设置
放置一个空的bootstrap.yml,然后在项目启动时设置默认的参数 springApplication.setDefaultProperties(properties)
想个办法动态的构建 bootstrap.yml 文件就是说项目启动时在 classpath下生成一个 bootstrap.yml 文件
启动参数:
java jar 0.0.1-SNAPSHOT.jar --spring.cloud.bootstrap.location=classpath:/local/bootstrap.yml
这样是可以的,不同的环境不同的启动参数
大家也看到了,这样话需要配置idea,让其识别,否则开发时代码没提示啊,不爽
利用spring boot的加载顺序进行改变
这是我从网上找的spring boot 加载顺序
那么我们可以手动设置
还是一样classpath下放一个空的 bootstrap.yml 文件
将配置放在 ${env}.properties文件中
resource
- bootstrap.yml
- local.properties
- test.properties
- prod.properties
在项目启动类如下
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication springApplication = new SpringApplication(Application.class)
// 虚拟机参数指定部署环境
String profile = System.getProperty("spring.profiles.active");
if (StringUtils.isEmpty(profile)){
throw new RuntimeException("未指定构建环境");
log.info("构建环境 ==> {}", profile);
Properties properties1 = new Properties();
properties1.load(EcoIpadApplication.class.getClassLoader().getResourceAsStream(profile + ".properties"));
springApplication.setDefaultProperties(properties1);
springApplication.run(args);
java -Dspring.profiles.active=local jar 0.0.1-SNAPSHOT.jar
代码也有提示(实在想不出来编的)
项目原来的yml或者开发者喜欢用yml的这种格式的会感觉不舒服
这种方案实现起来很多解决思路
项目启动时复制指定环境的配置文件重命名为 bootstrap.yml
maven打包时利用插件将文件重定向
maven方案(推荐)
pom 文件
<profiles>
<profile>
<!-- 本地开发环境 -->
<id>local</id>
<properties>
<env>local</env>
</properties>
<activation>
<!-- 设置默认激活这个配置 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<!-- 发布环境 -->
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
<profile>
<!-- 测试环境 -->
<id>test</id>
<properties>
<env>test</env>
</properties>
</profile>
</profiles>
<build>
<plugins>
<!--将指定环境的yml文件重命名为bootstrap.yml-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<move file="${project.build.directory}/classes/application-${env}.yml"
tofile="${project.build.directory}/classes/bootstrap.yml" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
这样maven打包时
mvn clean install -Dmaven.test.skip=true -Ptest
打包完target/classes如下
这也是我现在用的方案
既没有硬编码又使用了yml格式
维护起来不方便,要是接手项目的人不懂又没写好注释把这个插件删了,那就凉了
硬编码方案
public class Application {
public static void main(String[] args) throws Exception {
String profile = System.getProperty("spring.profiles.active");
if (StringUtils.isEmpty(profile)){
throw new RuntimeException("未指定构建环境");
log.info("构建环境 ==> {}", profile);
// 获取项目构建路径
// 这里想不到方案直接把文件放到 classpath 下,linux环境下不行,所以退而求其次,放到外部的文件夹
String property = System.getProperty("user.dir");
Resource resource = new DefaultResourceLoader().getResource("classpath:" + String.format("application-%s.yml", profile));
File target = new File(property + File.separator + "bootstrap.yml");
if (target.exists()){
target.delete();
Files.copy(resource.getInputStream(), target.toPath());
if (target.exists()){
List<String> collect = Stream.of(args).collect(Collectors.toList());
// 指定配置文件地址
collect.add("--spring.cloud.bootstrap.location=" + target.getPath());
args = collect.toArray(new String[0]);
其实还有一种方案,基于分支构建,但是改动小
利用yml多环境配置
server:
port: 8688
spring:
profiles:
active: test
#测试环境
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8601/eureka/
instance:
prefer-ip-address: true
spring:
application:
name: cccc
cloud:
config:
discovery:
enabled: true
service-id: config
profile: dev,redis
label: master
profiles: test
# 正式环境
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8601/eureka/
instance:
prefer-ip-address: true
spring:
application:
name: cccc
cloud:
config:
discovery:
enabled: true
service-id: config
profile: dev,redis
label: master
profiles: prod
合并代码时只要小心
spring:
profiles:
active: test
其实一开始的问题应该还有更优雅的解决方案,只是我没找到,还有其他方案的朋友可以留个言让我学习下,谢谢