纲要
- 环境与
Profile概念解析 - 多环境配置架构
application.yml作为公共配置application-{profile}.yml实现环境隔离
- 环境变量读取与默认值设置
- 数据库等中间件的差异化配置
- 激活 Profile 的多种方式
- 配置文件内
spring.profiles.active - 环境变量控制
- IDE 启动参数指定
- 配置文件内
- 生产环境安全实践
- 禁止自动 DDL
- 敏感信息外部化
- 日志精简化
- 基于 Docker 的 MySQL 环境搭建
- 完整可运行示例
环境与环境变量概述
在 Spring 生态中,一个应用往往需要运行在不同的上下文里:开发者在本地使用 localhost:8080 进行调试,测试团队在专有服务器上验证功能,最终交付到生产环境供真实用户访问。这些上下文就是所谓的"环境"。除了开发(dev)、测试(test)、生产(prod)三大基础环境外,企业还可能引入预发布(staging)、持续集成(CI)等环境。
Spring 为此提供了 Profile 机制,允许开发者将不同环境下的配置拆分成独立文件,并通过灵活的激活方式在运行时切换,从而避免硬编码或重复修改配置。
多配置文件结构
一个典型的 Spring Boot 项目会包含以下配置层次:
dir
src/main/resources/
├── application.yml # 各环境通用配置
├── application-dev.yml # 开发环境
├── application-test.yml # 测试环境
└── application-prod.yml # 生产环境
application.yml:存放所有环境共享的属性,比如应用名称、某些固定的日志级别、公共的message配置等。application-{profile}.yml:按照环境后缀拆分,如application-dev.yml,仅写入该环境特有的或需要覆盖的配置。
示例
yml
# application.yml (公共部分)
spring:
application:
name: security-oauth2-demo
messages:
basename: i18n/messages
server:
port: 8080
yml
# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
show-sql: true
hibernate:
ddl-auto: update
devtools:
restart:
enabled: true
debug: true
yml
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/security?useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${DB_USER:prod_user}
password: ${DB_PASSWORD}
jpa:
show-sql: false
hibernate:
ddl-auto: none
devtools:
restart:
enabled: false
logging:
level:
root: WARN
从以上配置可以看出,开发环境使用了嵌入式 H2 数据库并开启 SQL 显示,而生产环境连接 MySQL 且关闭了 DDL 自动更新和详细日志,这些都是符合环境职责的配置。
使用环境变量注入敏感信息
在 application-prod.yml 中,数据库用户名和密码采用了 ${DB_USER:prod_user} 的形式。其含义是:
- 优先读取操作系统环境变量
DB_USER的值; - 若该环境变量不存在,则使用默认值
prod_user。
这样的设计带来了两个明显好处:
- 一次构建,多处部署:同一份 jar 包分发到不同主机时,只要各主机预先设置好对应的环境变量,应用就能自动适配,无需修改配置文件。
- 安全隔离:生产数据库密码、API 密钥等敏感信息不会出现在代码仓库中。运维人员仅负责在服务器上设置环境变量,开发人员无权限接触生产机密。
同样,激活哪个 Profile 也可以通过环境变量动态指定:
yml
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
如果系统变量 SPRING_PROFILES_ACTIVE 未设置,则默认激活 dev 环境。
激活 Profile 的三种方式
实际开发中,我们可以根据需要选择激活方式:
-
配置文件直接写入(适合本地固定环境):
ymlspring: profiles: active: dev -
环境变量动态注入(适合容器化部署):
bashexport SPRING_PROFILES_ACTIVE=prod java -jar app.jar -
IDE 运行配置 (IntelliJ IDEA):
在
Run/Debug Configurations的 Spring Boot 启动项中,于Active profiles输入框填写目标环境,如prod。
数据库环境差异与初始化脚本
开发环境通常允许自动建表,并可以执行 data.sql 或 schema.sql 来初始化数据。但在生产环境中,这种做法必须被禁止:
spring.jpa.hibernate.ddl-auto设为none,防止表结构被意外修改。spring.sql.init.mode仅在嵌入式数据库时默认执行脚本,对外部数据库建议手动管理。
如果项目需要对接生产库,应通过 SQL 脚本在受控流程中建表:
sql
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
password VARCHAR(200) NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE TABLE roles (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL
);
通过数据库客户端或 Flyway/Liquibase 等迁移工具执行,确保表结构与实体映射保持一致。
Docker 快速搭建 MySQL 环境
为了方便本地验证生产环境的配置,我们使用 Docker Compose 启动一个 MySQL 实例:
yml
# docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: security
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
volumes:
mysql_data:
启动命令:
bash
docker-compose up -d
待容器就绪后,使用数据库工具连接 localhost:3306,执行建表及初始数据脚本。
完整示例:Spring Security + OAuth2 多环境启动
以下是一个整合了 Spring Security 与 OAuth2 授权服务的配置片段,展示如何根据 Profile 控制安全策略。开发环境为了方便测试可能关闭某些安全拦截,而生产环境严格启用。
java
// SecurityConfig.java
package com.example.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
@Profile("dev")
public SecurityFilterChain devFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll()
)
.csrf().disable();
return http.build();
}
@Bean
@Profile("prod")
public SecurityFilterChain prodFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/oauth/token").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer().jwt();
return http.build();
}
}
启动应用前,确认激活的 Profile 与数据库已正确配置。若激活 prod,则应用连接 MySQL 并加载生产级安全规则;若为 dev,则使用 H2 并开放所有端点。
环境差异对比
| 配置项 | 开发环境 | 生产环境 |
|---|---|---|
| 数据库类型 | H2 嵌入式 | MySQL 8.0 |
| DDL 策略 | update | none |
| SQL 日志 | true | false |
| DevTools | 启用 | 禁用 |
| 日志级别 | DEBUG | WARN |
| 安全策略 | 全部放行(方便调试) | 基于 OAuth2 资源服务器严格认证 |
流程总结
下面用 Mermaid 时序图展示一次部署切换 Profile 并读取环境变量的过程:
数据库 操作系统环境变量 Spring Boot 应用 开发者 数据库 操作系统环境变量 Spring Boot 应用 开发者 #mermaid-svg-40cIXoDRWRKKoGAk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-40cIXoDRWRKKoGAk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-40cIXoDRWRKKoGAk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-40cIXoDRWRKKoGAk .error-icon{fill:#552222;}#mermaid-svg-40cIXoDRWRKKoGAk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-40cIXoDRWRKKoGAk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-40cIXoDRWRKKoGAk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-40cIXoDRWRKKoGAk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-40cIXoDRWRKKoGAk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-40cIXoDRWRKKoGAk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-40cIXoDRWRKKoGAk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-40cIXoDRWRKKoGAk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-40cIXoDRWRKKoGAk .marker.cross{stroke:#333333;}#mermaid-svg-40cIXoDRWRKKoGAk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-40cIXoDRWRKKoGAk p{margin:0;}#mermaid-svg-40cIXoDRWRKKoGAk .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-40cIXoDRWRKKoGAk text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-40cIXoDRWRKKoGAk .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-40cIXoDRWRKKoGAk .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-40cIXoDRWRKKoGAk .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-40cIXoDRWRKKoGAk .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-40cIXoDRWRKKoGAk #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-40cIXoDRWRKKoGAk .sequenceNumber{fill:white;}#mermaid-svg-40cIXoDRWRKKoGAk #sequencenumber{fill:#333;}#mermaid-svg-40cIXoDRWRKKoGAk #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-40cIXoDRWRKKoGAk .messageText{fill:#333;stroke:none;}#mermaid-svg-40cIXoDRWRKKoGAk .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-40cIXoDRWRKKoGAk .labelText,#mermaid-svg-40cIXoDRWRKKoGAk .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-40cIXoDRWRKKoGAk .loopText,#mermaid-svg-40cIXoDRWRKKoGAk .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-40cIXoDRWRKKoGAk .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-40cIXoDRWRKKoGAk .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-40cIXoDRWRKKoGAk .noteText,#mermaid-svg-40cIXoDRWRKKoGAk .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-40cIXoDRWRKKoGAk .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-40cIXoDRWRKKoGAk .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-40cIXoDRWRKKoGAk .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-40cIXoDRWRKKoGAk .actorPopupMenu{position:absolute;}#mermaid-svg-40cIXoDRWRKKoGAk .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-40cIXoDRWRKKoGAk .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-40cIXoDRWRKKoGAk .actor-man circle,#mermaid-svg-40cIXoDRWRKKoGAk line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-40cIXoDRWRKKoGAk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动命令 java -jar app.jar 读取 SPRING_PROFILES_ACTIVE 返回 prod 激活 prod profile 读取 DB_USER, DB_PASSWORD 返回对应值 使用读取的凭据连接 连接成功 加载 application-prod.yml 配置 启动完成,提供 OAuth2 认证服务
总结
本文基于 Spring Security + OAuth2 的项目背景,深入讲解了如何利用 Spring 的 Profile 机制实现多环境配置分离,并结合环境变量保证敏感信息的安全性与部署的灵活性。
从配置文件结构、Profile 激活方式,到数据库差异化和 Docker 化基础设施,完整覆盖了从开发到生产的全链路实践。理解并合理运用这些能力,是构建健壮、可维护的企业级 Java 应用的重要一步。