SpringBoot多環(huán)境動態(tài)環(huán)境切換方式(nacos)
多環(huán)境配置說明:
在項目實際開發(fā)過程中,可能會有不同的環(huán)境,例如開發(fā)環(huán)境,測試環(huán)境和生產(chǎn)環(huán)境。不同的環(huán)境,對應(yīng)的配置信息是不同的,將項目發(fā)布到不同的環(huán)境,需要去更改對應(yīng)環(huán)境的配置信息,如果每次都是手動去更改環(huán)境,非常不友好,且容易漏掉配置,如果能夠?qū)崿F(xiàn)不同環(huán)境的自動識別,動態(tài)切換,將極大的提高工作效率。下面介紹一下自己在工作中使用到的多環(huán)境配置方法。
1. 環(huán)境變量切換
SpringBoot打包服務(wù)時,一些參數(shù)需要從外界獲取,可以通過在配置文件中配置環(huán)境變量實現(xiàn)
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:table}?useUnicode=true&characterEncoding=UTF-8
username: ${DB_USER:root}
password: ${DB_PASSWORD:root}
上面案例中,${DB_HOST:localhost} 服務(wù)會先在系統(tǒng)環(huán)境變量 DB_HOST 中獲取值,如果獲取不到則使用默認(rèn)值 localhost。
? 我們可以利用系統(tǒng)環(huán)境變量讀取機制,在不同環(huán)境下給變量配置不同值,通過讀取對應(yīng)變量的值,獲取不同的運行配置文件:
1.1 建立各環(huán)境配置文件
分別為 application-dev.yml 和 application-prod.yml 分別代表開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置:
# application.yml
spring:
profiles:
active: ${HUACHUN_PROFILES_ACTIVE:dev}
# application-dev.yml
server:
port: 9001
# application-prod.yml
server:
port: 9002這里只簡單的測試環(huán)境切換服務(wù)端口號
1.2 設(shè)置環(huán)境變量

網(wǎng)上有示例配置和系統(tǒng)變量后IDEA自動會有環(huán)境配置,我的沒有所以手動加上(可能是我使用的破解版吧 ,不知道是不是這個原因...)

如果使用的 linux 物理機, 使用 vim /etc/profile 命令,添加上一下配置
SPRING_PROFILES_ACTIVE=prod export SPRING_PROFILES_ACTIVE
1.3 啟動服務(wù)

2. nacos配置中心動態(tài)切換
通過環(huán)境變量的方式,不同環(huán)境的配置可以直接觀察到,不太安全(當(dāng)然可以將關(guān)鍵配置抽離成環(huán)境變量)。
可以將配置文件全部放置在 nacos 中,然后通過統(tǒng)一的域名訪問 nacos 服務(wù)器,在不同的環(huán)境配置域名對應(yīng)的host ,指向不同的nacos服務(wù)器地址,從而讀取對應(yīng)的配置。
2.1 配置文件
# application.yml
spring:
profiles:
active: ${HUACHUN_PROFILES_ACTIVE}
# application-prod.yml
nacos:
config:
server-addr: nacos_addr:port # 這里填寫相應(yīng)環(huán)境nacos配置中心地址和端口號
bootstrap:
enable: true
log-enable: true
data-ids: environment.yaml
type: yaml
group: dev
auto-refresh: true
# namespace: 0e1ff256-8d17-4e94-a4dd-d81c06e9b56c # 如果需要指定環(huán)境可以添加namespace屬性2.2 nacos配置

nacos中 environment.yaml 配置如下:
server: port: 9004
2.3 啟動服務(wù)
服務(wù)啟動后發(fā)現(xiàn)已經(jīng)從nacos讀取到服務(wù)端口號

訪問服務(wù)測試正常
3. 同一nacos環(huán)境下服務(wù)不同環(huán)境控制
上面兩種方式都是不同的環(huán)境對應(yīng)不同的配置中心,如果多個環(huán)境都是使用的同一個配置中心(使用nacos作為配置中心),那么可以通過 dataId 實現(xiàn)不同環(huán)境的隔離。
3.1 cloud方式
3.1.1 引入依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!-- 啟動獲取配置中心配置 原文鏈接:https://blog.csdn.net/chy2z/article/details/119464497 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.0.3</version>
</dependency>如果不引用spring-cloud-starter-bootstrap則不會讀取nacos配置
3.1.2 添加配置
添加 bootstrap.yml 配置文件
spring:
profiles:
active: ${HUACHUN_PROFILES_ACTIVE}
cloud:
nacos:
config:
server-addr: nacos_addr:port # nacos服務(wù)訪問地址和端口
enabled: true
file-extension: yaml
prefix: environment
group: dev
refresh-enabled: truenacos配置三個文件,端口好分別是9904,9908,9909(這里主要是快速簡單的測試讀取配置)

3.1.3 添加環(huán)境變量

3.1.4 啟動服務(wù)

3.1.5 讀取數(shù)據(jù)庫配置
1. 配置如下
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${env.project.db.username}
password: ${env.project.db.password}
url: jdbc:mysql://${env.project.db.adrr}:${env.project.db.port}/hhmt_cpa?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
type: com.alibaba.druid.pool.DruidDataSource2. nacos配置


訪問正常
4.Maven方式
4.1 創(chuàng)建配置文件
application-dev.yml 和 application-prod.yml 分別代表開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置
# application.yml
spring:
profiles:
active: ${HUACHUN_PROFILES_ACTIVE:dev}
# application-dev.yml
server:
port: 9001
# application-prod.yml
server:
port: 90024.2 更改pom文件
<profiles>
<profile>
<id>dev</id>
<properties>
<profileActive>dev</profileActive>
</properties>
<!--默認(rèn)開發(fā)環(huán)境-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
<properties>
<profileActive>test</profileActive>
</properties>
</profile>
<profile>
<id>pro</id>
<properties>
<profileActive>pro</profileActive>
</properties>
</profile>
</profiles>
4.3 maven打包
maven -P dev???????
5.配置加密
5.1 服務(wù)配置文件中加密
通常在連接數(shù)據(jù)庫時候明文信息不太安全,所以需要加密,這里展示數(shù)據(jù)庫連接用戶名密碼加解密,其他參數(shù)同理
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: JAS(sNhUs2TnBigaKiBTIeobYA==)
password: JAS(BaqtsIAiaTrZx84TJaMWAq2ryBPvvKbl)5.1.1 引入依賴
<!-- web應(yīng)用依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- cloud整合nacos依賴 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!-- cloud啟動依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.0.3</version>
</dependency>
<!-- mybatis-plus依賴 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- mysql依賴 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
</dependency>
<!-- 數(shù)據(jù)庫連接池依賴 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-spring.version}</version>
</dependency>
<!-- lombok依賴 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 加解密依賴 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>5.1.2 加解密工具類
package com.huachun.utils;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
public class JasyptUtil {
/**
* 加密方法
*
* @param password jasypt所需要的加密密碼配置
* @param value 需要加密的密碼
*/
public static String encyptPwd(String password, String value) {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
String result = encryptor.encrypt(value);
return result;
}
/**
* 解密
*
* @param password jasypt所需要的加密密碼配置
* @param value 需要解密的密碼
*/
public static String decyptPwd(String password, String value) {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
String realV = getValue(value);
String result = encryptor.decrypt(realV);
return result;
}
public static String getValue(String value) {
if (value.contains("JAS")) {
return value.substring(4, value.length() - 1);
}
return value;
}
}
5.1.3 添加監(jiān)聽
package com.huachun.listener;
import com.huachun.utils.JasyptUtil;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EnvironmentPreparedListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment env = event.getEnvironment();
MutablePropertySources pss = env.getPropertySources();
List<PropertySource> list = new ArrayList<>();
for (PropertySource ps : pss) {
Map<String, Object> map = new HashMap<>();
if (ps instanceof OriginTrackedMapPropertySource) {
OriginTrackedMapPropertySource propertySource = new OriginTrackedMapPropertySource(ps.getName(), map);
Map<String, Object> src = (Map<String, Object>) ps.getSource();
src.forEach((k, v) -> {
String strValue = String.valueOf(v);
if (strValue.startsWith("JAS(") && strValue.endsWith(")")) {
v = JasyptUtil.decyptPwd(k, v.toString());
}
map.put(k, v);
});
list.add(propertySource);
}
}
/**
此處是刪除原來的 OriginTrackedMapPropertySource 對象,
把解密后新生成的放入到 Environment,為什么不直接修改原來的
OriginTrackedMapPropertySource 對象,此處不做過多解釋
不懂的可以去看看它對應(yīng)的源碼,也算是留一個懸念,也是希望大家
能夠沒事多看一看源碼。
*/
list.forEach(ps -> {
pss.remove(ps.getName());
pss.addLast(ps);
});
}
}5.1.4 添加控制類
package com.huachun.controller;
import com.huachun.model.HcTest;
import com.huachun.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/env")
public class EnvCdDbController {
@Autowired
private TestService testService;
@GetMapping("/{id}")
public HcTest env(@PathVariable("id") String id) {
return testService.getData(id);
}
}
5.1.5 訪問測試

訪問成功,查詢數(shù)據(jù)庫沒有問題,說明解密成功。也可以打斷點進去看
5.2 SpringCloud讀取Nacos中加密參數(shù)
雖然在服務(wù)配置文件中可以正常加解密,但是并不靈活,通常還是需要將參數(shù)信息放到nacos配置中心管理。
總結(jié)
1.springboot方式使用application.yml配置
2.springcloud方式使用bootstrap.yml配置
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于Java實體類Serializable序列化接口的作用和必要性解析
序列化是將對象狀態(tài)轉(zhuǎn)化為可保持或者傳輸?shù)母袷竭^程,與序列化相反的是反序列化,完成序列化和反序列化,可以存儲或傳輸數(shù)據(jù),一般情況下,在定義實體類時會使用Serializable,需要的朋友可以參考下2023-05-05
Spring事務(wù)&Spring整合MyBatis的兩種方式
這篇文章主要介紹了Spring事務(wù)&Spring整合MyBatis的兩種方式,本文結(jié)合實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02
Java線程池ThreadPoolExecutor源碼深入分析
ThreadPoolExecutor作為java.util.concurrent包對外提供基礎(chǔ)實現(xiàn),以內(nèi)部線程池的形式對外提供管理任務(wù)執(zhí)行,線程調(diào)度,線程池管理等等服務(wù)2022-08-08

