导语

整套 SCA 实战项目完整源码已开源上传至 Gitee,可直接克隆、运行、学习:

源码仓库地址:https://gitee.com/xiaoyuancode/sca-study-demo

上一章我们完成了 Nacos 基础的服务注册和远程配置读取,本章落地企业通用多环境隔离方案
基于 module-nacos-env-demo 模块实现 dev、test、prod 三套环境完全隔离,通过 Profile、Namespace、Group 三维度区分环境,彻底解决开发、测试、生产配置互相干扰的问题。

技术版本

  • JDK 17
  • Spring Boot 3.5.0
  • Spring Cloud Alibaba 2025.0.0.0
  • Nacos 3.0.3

高频面试对比:Nacos 多环境隔离 vs 传统 Spring Profile

面试常问:只用 Spring 原生 Profile 能不能替代 Nacos 环境隔离?这里直接对比核心考点:

对比维度 仅 Spring Profile Nacos Namespace+Group+Profile
隔离力度 只隔离本地文件,远端共用一套配置 本地 + 远端完全隔离,各环境独立配置
配置安全性 生产配置写在代码内,极易泄露 远端统一管理,本地只做环境切换
团队协作 多人共用一套远端配置,容易覆盖 不同环境不同命名空间,互不干扰
运维友好度 打包需改配置,上线风险高 无需改代码,切换参数即可换环境
企业落地 仅适合个人本地学习调试 互联网公司标准微服务环境方案

面试小结:线上项目不会单纯依赖 Spring Profile,标准实现一定是 Nacos 远端三层隔离搭配 Spring Profile 本地区分,也是面试高频考点。

模块核心功能

本文示例仅用于本地学习演示,一键即可完整跑通多环境隔离流程:

  1. 使用 spring.config.import 统一拉取 Nacos 远端配置,避免配置分散

  2. 单文件 yml 用 --- 分段存放dev/test/prod,修改active即可一键切换,端口、命名空间、分组自动生效

  3. 通过 @ConfigurationProperties 批量绑定分层配置,数据库、Redis、业务参数统一封装

  4. 提供多个测试接口,可随时查看当前环境所有配置

  5. 支持配置动态刷新,改 Nacos 配置不用重启服务

  6. 实现 Profile+Namespace+Group 三维隔离

补充说明:本文将三套环境写在同一个 application.yml仅适合本地学习调试,目的是降低新手切换环境的操作成本;正规线上项目不会采用这种写法,下文单独给出符合 SpringBoot3 官方规范的企业标准部署方案,两种方案底层隔离逻辑完全一致。

一、Nacos 控制台前置准备

1. 创建三套独立命名空间 dev/test/prod

  1. 登录Nacos控制台:http://127.0.0.1:8080
    Nacos控制台登录页面

  2. 左侧菜单找到[命名空间],点击[新建命名空间]
    Nacos控制台命名空间

  3. 依次创建命名空间 dev、test、prod,描述分别填写:开发/测试/生产环境,保存完成。

2. 切换对应命名空间,分别创建同名配置

dev、test、prod 三个命名空间都需要创建一份完全Data ID完全相同的配置,循环操作3次即可:

  1. 左侧菜单栏选择[配置管理-配置列表],顶部切换命名空间(先dev,创建完后切test,最后切prod)
  2. 统一填写基本参数:
    • Data Id:nacos-env-demo.yaml
    • 分组:dev填DEFAULT_GROUP、test填TEST_GROUP、prod填PROD_GROUP
    • 配置格式:YAML
  3. 粘贴下方远端配置模板内容,点击发布保存
Nacos 远端统一配置模板
# 各环境通用配置模板
# dev/test/prod 命名空间均可粘贴,根据环境修改数据库、Redis地址
env:
  name: development
  description: 开发环境 - 用于本地开发和调试
  version: 1.0.0-SNAPSHOT

  # 数据库配置
  database:
    url: jdbc:mysql://localhost:3306/dev_db?useSSL=false&serverTimezone=Asia/Shanghai
    username: dev_user
    password: dev_password
    pool-size: 10

  # Redis配置
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0

  # 业务配置
  business:
    app-id: dev-app-001
    app-key: dev-secret-key-12345
    debug: true
    timeout: 30000

补充:企业线上标准多环境落地方案(SpringBoot3官方规范,弃用bootstrap.yml)

核心规范

禁止在任何 yml 硬编码 namespacegroupserver.portspring.profiles.active;

所有环境差异化参数全部通过命令行参数 / 操作系统环境变量注入,优先级高于所有yml配置,启动第一时间生效,完美规避加载时序 bug,无需引入bootstrap相关依赖。

  1. 项目只保留一份application.yml文件,删除 application-dev.yml / application-test.yml /application-prod.yml
# application.yml 只存放全环境通用固定配置
spring:
  application:
    name: nacos-env-demo
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8090
      config:
        server-addr: 127.0.0.1:8090
        file-extension: yaml
  config:
    import: nacos:nacos-env-demo.yaml

logging:
  level:
    com.alibaba.cloud.nacos: DEBUG
    org.springframework.cloud: DEBUG

2.pom必须配置SpringBoot打包插件(解决java -jar提示无主清单属性报错)

<project>
    <dependencies>
    <!-- 原有依赖不动 -->
    </dependencies>
    <!-- 新增build插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

3.三种线上部署启动方式

① Linux 服务器 shell 启动脚本(中小公司传统部署)

流程说明:

  • 本地开发机执行mvn clean package -DskipTests生成jar包;
  • 仅上传target目录下的jar包到服务器,服务器不存放源码、无Maven环境;
  • 直接前台执行 java -jar 仅适合临时测试,SSH连接关闭后进程会直接销毁;正式线上必须使用 nohup 后台常驻,持久化输出日志。

start-dev.sh

#!/bin/bash
# dev环境线上常驻启动脚本
JAR_NAME="module-nacos-env-demo-1.0-SNAPSHOT.jar"
LOG_NAME="nacos-env-dev.log"

nohup java -jar ${JAR_NAME} \
--spring.profiles.active=dev \
--spring.cloud.nacos.config.namespace=dev \
--spring.cloud.nacos.config.group=DEFAULT_GROUP \
--server.port=8081 \
> ${LOG_NAME} 2>&1 &

echo "dev服务已后台启动,PID: $!"
echo "日志路径:./${LOG_NAME}"

start-test.sh、start-prod.sh 仅替换 namespace、group、port、日志文件名即可。

配套运维常用命令

# 实时滚动查看日志
tail -f nacos-env-dev.log
# 检索服务运行进程
ps -ef | grep nacos-env
# 优雅停止服务(优先推荐)
# 不建议直接使用 kill -9 强制杀死进程,会丢失请求与事务
kill -15 进程PID

② Docker 容器部署

打包构建镜像仅在本地 / CI 流水线执行一次,容器运行时只启动 Jar,不编译打包

Mac 本地测试注意:容器访问宿主机 Nacos 需使用 host.docker.internal,不能填写 127.0.0.1

线上Linux服务器访问宿主机Nacos不可使用host.docker.internal,需填写宿主机的IP地址

docker run -d \
-e SPRING_PROFILES_ACTIVE=prod \
-e SPRING_CLOUD_NACOS_CONFIG_NAMESPACE=prod \
-e SPRING_CLOUD_NACOS_CONFIG_GROUP=PROD_GROUP \
-e SPRING_CLOUD_NACOS_DISCOVERY_SERVER-ADDR=host.docker.internal:8090 \
-e SPRING_CLOUD_NACOS_CONFIG_SERVER-ADDR=host.docker.internal:8090 \
-p 8083:8083 \
--name nacos-prod nacos-env-demo:1.0

③ K8s 云原生部署(大厂标准)

打包、镜像推送交给 CI 流水线,K8s 仅调度成品镜像,通过环境变量注入环境标识

env:
  - name: SPRING_PROFILES_ACTIVE
    value: "prod"
  - name: SPRING_CLOUD_NACOS_CONFIG_NAMESPACE
    value: "prod"
  - name: SPRING_CLOUD_NACOS_CONFIG_GROUP
    value: "PROD_GROUP"

二、项目 Maven 依赖 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.xiaoyuancode</groupId>
        <artifactId>sca-study</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>module-nacos-env-demo</artifactId>
    <name>module-nacos-env-demo</name>
    <description>Nacos多环境隔离实战 - Profile + Namespace + Group</description>

    <dependencies>
        <!-- SpringBoot Web 基础依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Nacos 服务注册发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- Nacos 配置中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!-- 配置属性绑定处理器,IDE配置提示 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <!-- SpringBoot 打包插件,生成可执行Jar -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

三、本地学习专用 application.yml

单文件完成三套环境隔离,只改第一处 active 参数即可切换环境,非常方便:

spring:
  application:
    name: nacos-env-demo

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml

  config:
    import: nacos:nacos-env-demo.yaml

  profiles:
    active: prod # 修改此处 dev/test/prod 一键切换环境

logging:
  level:
    com.alibaba.cloud.nacos: DEBUG
    org.springframework.cloud: DEBUG

# 开发环境
---
spring:
  config:
    activate:
      on-profile: dev
  cloud:
    nacos:
      config:
        namespace: dev
        group: DEFAULT_GROUP
server:
  port: 8081

# 测试环境
---
spring:
  config:
    activate:
      on-profile: test
  cloud:
    nacos:
      config:
        namespace: test
        group: TEST_GROUP
server:
  port: 8082

# 生产环境
---
spring:
  config:
    activate:
      on-profile: prod
  cloud:
    nacos:
      config:
        namespace: prod
        group: PROD_GROUP
server:
  port: 8083

四、核心 Java 代码

1. 启动类 NacosEnvApplication.java

package com.xiaoyuancode.sca.nacos.env;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class NacosEnvApplication {
    public static void main(String[] args) {
        SpringApplication.run(NacosEnvApplication.class, args);
    }
}

2. 分层配置绑定类 EnvConfig.java

批量绑定 Nacos 远端 env 下所有参数,结构清晰,方便维护:

package com.xiaoyuancode.sca.nacos.env.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "env")
public class EnvConfig {

    private String name;
    private String description;
    private String version;

    private Database database = new Database();
    private Redis redis = new Redis();
    private Business business = new Business();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public Database getDatabase() {
        return database;
    }

    public void setDatabase(Database database) {
        this.database = database;
    }

    public Redis getRedis() {
        return redis;
    }

    public void setRedis(Redis redis) {
        this.redis = redis;
    }

    public Business getBusiness() {
        return business;
    }

    public void setBusiness(Business business) {
        this.business = business;
    }

    public static class Database {
        private String url;
        private String username;
        private String password;
        private int poolSize;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public int getPoolSize() {
            return poolSize;
        }

        public void setPoolSize(int poolSize) {
            this.poolSize = poolSize;
        }
    }

    public static class Redis {
        private String host;
        private int port;
        private String password;
        private int database;

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public int getDatabase() {
            return database;
        }

        public void setDatabase(int database) {
            this.database = database;
        }
    }

    public static class Business {
        private String appId;
        private String appKey;
        private boolean debug;
        private int timeout;

        public String getAppId() {
            return appId;
        }

        public void setAppId(String appId) {
            this.appId = appId;
        }

        public String getAppKey() {
            return appKey;
        }

        public void setAppKey(String appKey) {
            this.appKey = appKey;
        }

        public boolean isDebug() {
            return debug;
        }

        public void setDebug(boolean debug) {
            this.debug = debug;
        }

        public int getTimeout() {
            return timeout;
        }

        public void setTimeout(int timeout) {
            this.timeout = timeout;
        }
    }
}

3. 环境测试接口 Controller

package com.xiaoyuancode.sca.nacos.env.controller;

import com.xiaoyuancode.sca.nacos.env.config.EnvConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 多环境测试控制器
 * 展示如何读取不同环境的配置
 */
@RestController
@RequestMapping("/env")
public class EnvController {

    @Autowired
    private EnvConfig envConfig;

    @Autowired
    private Environment environment;

    /**
     * 获取当前环境基本信息
     */
    @GetMapping("/info")
    public Map<String, Object> getEnvInfo() {
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("activeProfile", environment.getProperty("spring.profiles.active"));
        result.put("envName", envConfig.getName());
        result.put("desc", envConfig.getDescription());
        result.put("version", envConfig.getVersion());
        return result;
    }

    /**
     * 获取数据库配置
     */
    @GetMapping("/database")
    public EnvConfig.Database getDatabaseConfig() {
        return envConfig.getDatabase();
    }

    /**
     * 获取Redis配置
     */
    @GetMapping("/redis")
    public EnvConfig.Redis getRedisConfig() {
        return envConfig.getRedis();
    }

    /**
     * 获取业务配置
     */
    @GetMapping("/business")
    public EnvConfig.Business getBusinessConfig() {
        return envConfig.getBusiness();
    }
}

五、本地启动方式

  1. 启动本地 Nacos,确保控制台可正常访问

  2. 按照前文步骤,在 Nacos 中创建 dev/test/prod 命名空间与对应配置

  3. 修改 application.ymlspring.profiles.active 指定运行环境

  4. IDEA 运行 NacosEnvApplication 启动项目

六、接口测试命令

切换不同环境后,用下面命令测试配置是否正常加载:

Windows10/Win11 自带 curl 可直接复制命令;Win7 / 旧版 Windows 可直接浏览器访问对应地址查看 JSON。

下面以 dev 环境 8081 端口演示,test 替换 8082、prod 替换 8083;核心校验接口为 /env/info,返回正常即代表多环境配置加载成功。

# 获取当前环境基础信息
curl http://localhost:8081/env/info

# 获取数据库配置
curl http://localhost:8081/env/database

# 获取Redis配置
curl http://localhost:8081/env/redis

# 获取业务配置
curl http://localhost:8081/env/business

接口返回数据

七、实操踩坑总结(本地真实遇到的问题)

  1. SpringBoot3 配置加载时序坑

    SpringBoot3 调整了配置加载优先级,如果将 Nacos 远程拉取配置写在分段环境配置内,会先执行远端拉取、后加载 namespace 和 group,默认读取 public 命名空间报错。

    本地单文件 — 分段写法可规避该问题;线上推荐启动参数注入环境标识根治。

  2. 本地激活环境与 Nacos 配置必须一一对应

    代码激活的 profile、Nacos 命名空间、分组三者必须匹配,否则远端配置无法正常拉取,新手极易出现配置错乱。

  3. @ConfigurationProperties 前缀必须严格匹配

    Nacos 配置最外层前缀为 env,代码注解 prefix = "env" 字符、大小写必须完全一致,否则所有配置字段全部注入 null。

  4. 前台 java -jar 线上运行进程丢失

    不加 nohup 前台启动,SSH 终端断开、会话超时会直接终止 Java 进程,生产环境必须使用 nohup 后台常驻脚本。

  5. java -jar 提示无主清单属性

    pom 文件缺少 spring-boot-maven-plugin 打包插件,执行 mvn clean package -DskipTests 不会生成可执行完整 Jar,补充插件后重新打包即可。

小结

这一章我们从零搭建了企业级 Nacos 多环境隔离工程,靠 Profile+Namespace+Group 三维隔离,彻底解决了开发、测试、生产配置混乱的问题,完全贴合实际工作和面试重点。
整套代码结构简洁、可直接复用在真实项目,同时支持动态刷新,实用性非常强。

下一章预告:Spring Cloud Alibaba实战03|OpenFeign远程调用+LoadBalancer负载均衡


作者简介

刘晓媛 | XiaoYuanCode

资深全栈开发,具备丰富前后端一体化业务系统落地经验。现阶段主攻 Java 微服务与 Spring AI,分享个人学习复盘与 RAG 工程化落地方案,所有案例均本地验证,配套代码可直接复用。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐