CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / JHIPSTER / P-236 · JHipster 项目开发

JHipster 开发 21:多环境配置管理

配置管理是每个项目上线前绕不开的一课。JHipster 在生成时就替你搭好了 dev/prod 两套 profile 骨架,但”骨架正确”和”团队用对”是两回事。我们组吃过配置的亏:测试环境连的库被人手滑改成生产、JWT secret 提交进了 git 导致全员换密钥。这篇把这些年的配置管理经验整理成一套可复制的做法。

JHipster 生成的配置结构

生成完成后的 src/main/resources/config/ 长这样:

src/main/resources/
├── application.yml                # 公共配置(所有 profile 共享)
├── config/
│   ├── application-dev.yml        # 开发:H2 内存库、热重载、mock 数据
│   ├── application-prod.yml       # 生产:真实数据源、监控、日志文件
│   ├── application-tls.yml        # 可选 TLS
│   └── liquibase/                 # 数据库 changelog
└── .well-known/

Spring Boot 4 延续了既有的 profile 机制:spring.profiles.active=dev 激活对应文件,未激活的配置块跳过。JHipster 默认在 application.yml 里写入:

spring:
  profiles:
    active: @spring.profiles.active@   # Maven resource filter 替换
    # explicit-user-config-requires-refresh 见官方文档
jhipster:
  registry:
    password: ${JHIPSTER_REGISTRY_PASSWORD:admin}

注意那行 @spring.profiles.active@——Maven 的 resource filtering 会在打包时按 -Pprod-Pdev 替换。所以运行 jar 时若不指定 profile,行为取决于打包时用的 profile,这是新手常见困惑点。我们团队约定:打生产包必须 ./mvnw -Pprod package verify,且启动脚本里显式传 --spring.profiles.active=prod 双保险。

dev 与 prod 的关键差异

配置项devprod备注
数据库H2 内存/文件PostgreSQL/MySQLprod 必须显式配置
Hibernate DDLcreate-drop(H2)validate + Liquibase生产严禁自动建表
Liquibase启动执行启动执行(异步可选)prod 可开 async-start
日志控制台 DEBUG文件 + 级别收敛到 INFO见下文日志路径
缓存ehcache 本地Redis(选型后)微服务必须分布式缓存
CORS全放开收紧到具体域名安全项
Swagger开启默认关闭生产按需网关暴露
metrics/health 暴露全量按需配合管理端口

一份典型的 application-prod.yml 片段:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:myapp}
    username: ${DB_USER}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 20
  jpa:
    hibernate:
      ddl-auto: validate
  liquibase:
    async-start: true

server:
  port: 8080

logging:
  file:
    name: /var/log/myapp/app.log
  level:
    root: INFO
    tech.myapp: INFO

jhipster:
  security:
    authentication:
      jwt:
        secret: ${JWT_SECRET}
        token-validity: 86400
  logging:
    use-json-format: true
    logstash:
      enabled: true

敏感信息不进 git 的三道防线

配置里的密码、secret、密钥,永远不该出现在版本库。我们有三条原则,层层兜底:

第一道:环境变量(默认手段)。 所有生成配置里敏感项都写成 ${DB_PASSWORD} 这种占位形式,值来自环境变量或启动参数。本地开发起服务:

export DB_PASSWORD=devpass
./mvnw -Pdev spring-boot:run

第二道:本地私有配置文件(开发体验)。 每人本机的私有配置放进 application-local.yml,配合 .gitignore

# 本地私有配置,不入库
src/main/resources/config/application-local.yml

启动时激活叠加:--spring.profiles.active=dev,local。注意 local 放在最后,优先级最高,可以覆盖任何 dev 默认值。

第三道:运行时密钥管理(生产)。 生产环境用 K8s Secret 或 Vault 注入,不给任何人明文:

# k8s deployment 片段
env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: myapp-db
        key: password
  - name: JWT_SECRET
    valueFrom:
      secretKeyRef:
        name: myapp-jwt
        key: secret
  - name: SPRING_PROFILES_ACTIVE
    value: prod
  - name: SPRING_CONFIG_ADDITIONAL_LOCATION
    value: file:/etc/myapp/   # 挂载的 Secret 目录

另外在 CI 里加一道防泄漏扫描(git-secrets 或 trufflehog),防止有人不小心 commit 了 .env 文件。JWT secret 尤其注意:至少 86 位随机 Base64(HS512 要求),我们被扫描器扫出弱 secret 后全部改成了 openssl rand -base64 192 生成。

配置优先级与覆盖关系

Spring Boot 的配置覆盖顺序(从低到高,高者生效):

优先级来源典型用途
1application.yml(jar 内)公共默认值
2application-{profile}.yml(jar 内)环境默认值
3jar 外 config/ 目录部署时覆盖
4SPRING_CONFIG_ADDITIONAL_LOCATIONK8s 挂载 Secret
5环境变量容器注入
6命令行参数 --xxx=yyy临时救急

几个实战推论:

  • 环境变量命名规则spring.datasource.password 对应 SPRING_DATASOURCE_PASSWORD(点换下划线、全大写)。
  • local profile 放最后dev,local 中 local 覆盖 dev,反过来则无效。
  • 命令行参数只用于临时:写进启动脚本前先想清楚它是否该进 application-prod.yml,散落的启动参数是配置漂移的起点。
  • JHipster 特有项jhipster.* 命名空间下的开关(缓存、安全、日志)同样遵循这套优先级。

微服务场景:Spring Cloud Config

微服务架构下(第 5 篇的 gateway + registry + 服务拓扑),配置问题从”一个应用两套环境”变成”八个服务 × 三套环境”。JHipster 微服务默认方案是注册中心自带配置分发:JHipster Registry(或 Consul)充当 Spring Cloud Config Server,各服务作为 client 启动时拉取配置。

# 微服务自身的 application.yml
spring:
  application:
    name: order-service
  config:
    import: optional:configserver:${JHIPSTER_CONFIG_SERVER:https://registry.mycompany.com/config}
  cloud:
    config:
      fail-fast: true
      retry:
        initial-interval: 1000
        max-attempts: 6

集中配置仓库的目录结构:

config-repo/(独立 git 仓库)
├── application.yml            # 所有服务共享
├── application-prod.yml       # 所有服务共享的生产覆盖
├── order-service.yml          # 订单服务专属
├── order-service-prod.yml
└── inventory-service.yml

这样改一个配置只需要在配置仓库提交,配合 /actuator/refresh 或 Spring Cloud Bus 动态下发,不用重打八个镜像。敏感信息仍不入库:Config Server 后端接 Vault,或在配置仓库里只放占位符、由各环境注入。

各环境配置 checklist

上线前对照检查(26、28 篇还会从性能与发布角度扩展):

检查项devtest/stagingprod
profile 显式指定可默认 dev必须显式必须显式 + 打包 -Pprod
数据源凭据来自环境变量建议必须必须(Secret/Vault)
JWT secret 强度不限必须 86+ 位必须 + 定期轮换预案
ddl-autocreate-drop 可validatevalidate
Liquibase 演练顺手跑全量演练升级前空库演练(第 7 篇)
日志落盘/聚合控制台落盘JSON + 聚合(第 20 篇)
CORS/管理端口放开收紧白名单 + 独立管理端口
默认 admin 账号可留改密禁用/改密(第 28 篇)

小结

  • JHipster 的 dev/prod 骨架是起点,团队约定才是配置管理的核心:打包 profile 与启动 profile 双显式。
  • 敏感信息三道防线:环境变量占位、local 私有 yml 不入库、生产 Secret/Vault 注入,外加 CI 防泄漏扫描。
  • 理解 Spring 配置优先级链,才能在”配置到底从哪来的”问题上少扯皮。
  • 微服务下尽早切到注册中心/Spring Cloud Config 的集中配置,服务数量翻倍前就要做。

系列导航

← 设计模式 021:备忘录(Memento) 目录 算法 022:图:基础和Overview →
← 返回文章列表