SpringBoot多环境动态环境切换方式(nacos)

 更新时间:2025年12月08日 09:15:27   作者:春卷-huachun  
本文介绍了多环境配置的几种方法,包括环境变量切换、Nacos配置中心动态切换、同一Nacos环境下服务不同环境控制、Maven方式配置以及配置加密

多环境配置说明:

在项目实际开发过程中,可能会有不同的环境,例如开发环境,测试环境和生产环境。不同的环境,对应的配置信息是不同的,将项目发布到不同的环境,需要去更改对应环境的配置信息,如果每次都是手动去更改环境,非常不友好,且容易漏掉配置,如果能够实现不同环境的自动识别,动态切换,将极大的提高工作效率。下面介绍一下自己在工作中使用到的多环境配置方法。

1. 环境变量切换

SpringBoot打包服务时,一些参数需要从外界获取,可以通过在配置文件中配置环境变量实现

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} 服务会先在系统环境变量 DB_HOST 中获取值,如果获取不到则使用默认值 localhost

​ 我们可以利用系统环境变量读取机制,在不同环境下给变量配置不同值,通过读取对应变量的值,获取不同的运行配置文件:

1.1 建立各环境配置文件

分别为 application-dev.yml 和 application-prod.yml 分别代表开发环境和生产环境的配置:

# application.yml
spring:
  profiles:
    active: ${HUACHUN_PROFILES_ACTIVE:dev}

# application-dev.yml
server:
  port: 9001

# application-prod.yml
server:
  port: 9002

这里只简单的测试环境切换服务端口号

1.2 设置环境变量

网上有示例配置和系统变量后IDEA自动会有环境配置,我的没有所以手动加上(可能是我使用的破解版吧 ,不知道是不是这个原因...)

如果使用的 linux 物理机, 使用 vim /etc/profile 命令,添加上一下配置

  SPRING_PROFILES_ACTIVE=prod
  export SPRING_PROFILES_ACTIVE

1.3 启动服务

2. nacos配置中心动态切换

通过环境变量的方式,不同环境的配置可以直接观察到,不太安全(当然可以将关键配置抽离成环境变量)。

可以将配置文件全部放置在 nacos 中,然后通过统一的域名访问 nacos 服务器,在不同的环境配置域名对应的host ,指向不同的nacos服务器地址,从而读取对应的配置。

2.1 配置文件

# application.yml
spring:
  profiles:
    active: ${HUACHUN_PROFILES_ACTIVE}

# application-prod.yml
nacos:
  config:
    server-addr: nacos_addr:port    # 这里填写相应环境nacos配置中心地址和端口号
    bootstrap:
      enable: true
      log-enable: true
    data-ids: environment.yaml
    type: yaml
    group: dev
    auto-refresh: true
#    namespace: 0e1ff256-8d17-4e94-a4dd-d81c06e9b56c   # 如果需要指定环境可以添加namespace属性

2.2 nacos配置

 nacos中 environment.yaml 配置如下:

server:
  port: 9004

2.3 启动服务

服务启动后发现已经从nacos读取到服务端口号

访问服务测试正常

3. 同一nacos环境下服务不同环境控制

上面两种方式都是不同的环境对应不同的配置中心,如果多个环境都是使用的同一个配置中心(使用nacos作为配置中心),那么可以通过 dataId 实现不同环境的隔离。

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服务访问地址和端口
        enabled: true
        file-extension: yaml
        prefix: environment
        group: dev
        refresh-enabled: true

nacos配置三个文件,端口好分别是9904,9908,9909(这里主要是快速简单的测试读取配置) 

3.1.3 添加环境变量

3.1.4 启动服务

3.1.5 读取数据库配置

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.DruidDataSource

2. nacos配置

 

访问正常

4.Maven方式

4.1 创建配置文件

application-dev.yml 和 application-prod.yml 分别代表开发环境和生产环境的配置

# application.yml
spring:
  profiles:
    active: ${HUACHUN_PROFILES_ACTIVE:dev}

# application-dev.yml
server:
  port: 9001

# application-prod.yml
server:
  port: 9002

4.2 更改pom文件

 <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <profileActive>dev</profileActive>
            </properties>
            <!--默认开发环境-->
            <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 服务配置文件中加密

通常在连接数据库时候明文信息不太安全,所以需要加密,这里展示数据库连接用户名密码加解密,其他参数同理

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: JAS(sNhUs2TnBigaKiBTIeobYA==)
    password: JAS(BaqtsIAiaTrZx84TJaMWAq2ryBPvvKbl)

5.1.1 引入依赖

        <!-- web应用依赖 -->
        <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>

        <!-- 数据库连接池依赖 -->
        <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 添加监听

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 对象,此处不做过多解释
         不懂的可以去看看它对应的源码,也算是留一个悬念,也是希望大家
         能够没事多看一看源码。
         */
        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 访问测试

访问成功,查询数据库没有问题,说明解密成功。也可以打断点进去看 

5.2 SpringCloud读取Nacos中加密参数

虽然在服务配置文件中可以正常加解密,但是并不灵活,通常还是需要将参数信息放到nacos配置中心管理。

总结

1.springboot方式使用application.yml配置

2.springcloud方式使用bootstrap.yml配置

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 关于Java实体类Serializable序列化接口的作用和必要性解析

    关于Java实体类Serializable序列化接口的作用和必要性解析

    序列化是将对象状态转化为可保持或者传输的格式过程,与序列化相反的是反序列化,完成序列化和反序列化,可以存储或传输数据,一般情况下,在定义实体类时会使用Serializable,需要的朋友可以参考下
    2023-05-05
  • JAVA使用geotools读取shape格式文件的方法

    JAVA使用geotools读取shape格式文件的方法

    这篇文章主要介绍了JAVA使用geotools读取shape格式文件的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2017-01-01
  • Spring事务&Spring整合MyBatis的两种方式

    Spring事务&Spring整合MyBatis的两种方式

    这篇文章主要介绍了Spring事务&Spring整合MyBatis的两种方式,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • Java多线程Atomic包操作原子变量与原子类详解

    Java多线程Atomic包操作原子变量与原子类详解

    这篇文章主要介绍了Java多线程Atomic包操作原子变量与原子类详解,简单介绍了Atomic,同时涉及java.util.concurrent中的原子变量,Atomic类的作用等相关内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • SpringBoot中的文件上传和异常处理详解

    SpringBoot中的文件上传和异常处理详解

    这篇文章主要介绍了SpringBoot中的文件上传和异常处理详解,对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息,对于浏览器客户端,响应一个"whitelabel"错误视图,以HTML格式呈现相同的数据,需要的朋友可以参考下
    2023-09-09
  • Java线程池ThreadPoolExecutor源码深入分析

    Java线程池ThreadPoolExecutor源码深入分析

    ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等等服务
    2022-08-08
  • Java实现锐化图片并保存功能(附源码)

    Java实现锐化图片并保存功能(附源码)

    在图像处理领域,锐化(Sharpening) 是一种常见的操作,用于增强图像中边缘和细节,使图像看起来更清晰,下面小编就来介绍一下如何使用Java SE 原生 API实现对图像的锐化处理并保存为常见格式文件吧
    2025-05-05
  • Java中接口和抽象类的异同以及具体的使用场景

    Java中接口和抽象类的异同以及具体的使用场景

    文章主要介绍了Java中接口(Interface)和抽象类(AbstractClass)的区别和联系,包括相同点和不同点,以及它们在实际开发中的具体使用场景,结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • SpringBoot整合Milvus的实现

    SpringBoot整合Milvus的实现

    本文主要介绍了SpringBoot整合Milvus的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • DTD验证xml格式的三种方式详解

    DTD验证xml格式的三种方式详解

    这篇文章主要介绍了DTD验证xml格式的三种方式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10

最新评论