乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Boot 概述

    • 章节导读:Spring Boot概述与核心理念
    • Spring Boot是什么
    • Spring Boot与Spring Framework的关系
    • 约定优于配置
  • 第2章 快速入门与第一个应用

    • 章节导读:快速入门与第一个应用
    • SpringApplication
    • 第一个Spring Boot应用
  • 第3章 起步依赖与版本管理

    • 章节导读:起步依赖与版本管理
    • 起步依赖
    • BOM版本管理
  • 第4章 自动配置原理

    • 章节导读:自动配置原理
    • 自动配置原理
    • AutoConfigurationImportSelector
    • 自动配置报告
  • 第5章 核心注解

    • 章节导读:核心注解
    • @SpringBootApplication
    • @EnableAutoConfiguration
    • @ConditionalOnClass
    • @ConditionalOnMissingBean
    • @ConditionalOnBean
    • @ConditionalOnProperty
  • 第6章 外部化配置与属性绑定

    • 章节导读:外部化配置与属性绑定
    • 外部化配置
    • @ConfigurationProperties
    • @Value
    • 配置属性优先级
  • 第7章 Profile 与环境切换

    • 章节导读:Profile与环境切换
    • @Profile
    • 多环境配置文件
  • 第8章 内嵌服务器与部署

    • 章节导读:内嵌服务器与部署
    • 内嵌服务器
    • Fat Jar
  • 第9章 Actuator 与监控

    • 章节导读:Actuator与监控
    • Actuator Health
    • Actuator Info
    • Actuator Metrics
    • 自定义Endpoint
    • 自定义HealthIndicator
  • 第10章 开发工具与最佳实践

    • 章节导读:开发工具与最佳实践
    • Banner自定义
    • 热部署

Profile

一句话定位:Profile 是 Spring Boot 的环境隔离与条件装配机制,允许为不同运行环境(dev / test / prod)定义独立的 Bean 和配置集,通过激活指令在启动时按需加载,实现"同一份代码、多套环境、一键切换"。


定义与作用

Profile 是 Spring Framework 的基础能力,Spring Boot 在其之上做了增强和自动化。它的核心思想是:为不同环境定义不同的 Bean 和配置,在应用启动时根据激活的 Profile 只加载对应的环境配置。

在飞翔科技的学生成绩管理系统中:

  • dev:使用 H2 内存数据库,调试日志全开,端口 8080
  • test:使用 MySQL 测试库,日志级别 WARN,端口 8081
  • prod:使用 MySQL 生产库,日志级别 ERROR,端口 80,关闭调试端点

没有 Profile 时,这些配置只能混杂在一个文件中,容易误把测试配置带到生产环境。

对比传统 SSM 的繁琐过程

维度传统 SSMSpring Boot Profile
环境切换手动修改 applicationContext.xml 中的 <bean> 定义一条命令或一个配置项自动切换
配置分离所有环境配置写在同一个 XML 中,注释切换每个环境独立文件或独立 YAML 文档
Bean 条件加载需要自定义 BeanDefinitionRegistryPostProcessor@Profile("dev") 注解自动处理
部署安全容易把测试配置部署到生产Profile 隔离,生产默认激活 prod

适用位置与常用属性

激活 Profile 的三种方式

方式优先级说明示例
命令行参数最高启动时传入,临时覆盖--spring.profiles.active=prod
application.yml中默认激活配置,可被覆盖spring.profiles.active: dev
JVM 参数高在启动脚本中固定-Dspring.profiles.active=test

在代码中使用 @Profile

@Profile 注解可以标注在:

  1. 类级别:标记该配置类只在指定 Profile 激活时生效
  2. @Bean 方法级别:标记该 Bean 只在指定 Profile 激活时注册
// 方式一:类级别
@Configuration
@Profile("dev")
public class DevConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
    }
}

// 方式二:方法级别
@Configuration
public class DataSourceConfig {
    
    @Bean
    @Profile("dev")
    public DataSource h2DataSource() {
        // H2 内存数据库
    }
    
    @Bean
    @Profile("!dev")  // "!dev" 表示非 dev 环境时生效
    public DataSource mysqlDataSource() {
        // MySQL 连接池
    }
}

常用属性

属性/配置键说明示例
spring.profiles.active当前激活的 Profile 列表,逗号分隔dev,local
spring.profiles.default未显式激活时使用的默认 Profiledev
spring.profiles.groupProfile 分组(Spring Boot 2.4+)production: proddb,prodmq
@Profile("dev")仅在 dev 环境激活时生效-
@Profile("!prod")在 prod 环境不激活时生效-
@Profile({"dev", "test"})在 dev 或 test 环境激活时生效-

核心原理

Profile 激活与 Bean 注册流程

图解释:Spring Boot 启动时,首先在 Environment 中确定激活的 Profile 列表。然后在容器刷新阶段扫描所有 Bean 定义,没有标注 @Profile 的 Bean 在所有环境都注册;标注了 @Profile 的 Bean 只在 Profile 匹配时注册,不匹配的 Bean 被跳过。

Profile 与配置文件的联动


完整示例

场景说明

飞翔科技的学生成绩管理系统需要三种数据源配置:

  • dev:H2 内存数据库,方便小崔本地开发时快速启动,无需安装 MySQL
  • test:MySQL 测试库,QA 团队使用,数据可持久化但隔离
  • prod:MySQL 生产库,线上环境,连接池参数优化

架构师白歌要求:使用 @Profile 实现条件化数据源,确保 dev 环境不会意外连接到生产数据库。

操作前:所有环境共用一套 DataSource,经常连错库

// 操作前:没有 Profile 隔离,所有环境共用同一个 DataSource
package com.feixiang.student.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
public class BadDataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        // 小崔在本地开发时写了 H2 的配置
        // 但测试同事拉下代码后运行,发现连不上数据库
        // 因为测试环境没有 H2,只有 MySQL
        // 小崔只能手动改代码再提交,导致代码仓库的配置不断变化
        return DataSourceBuilder.create()
            .driverClassName("org.h2.Driver")
            .url("jdbc:h2:mem:student_db")
            .username("sa")
            .password("")
            .build();
    }
}

痛点:

  • 所有环境共用同一个 Bean 定义,小崔和测试同事互相冲突
  • 数据库连接信息暴露在代码中,生产密码不安全
  • 无法做到"开箱即用"的 dev 体验和"安全隔离"的 prod 部署

使用 Profile 的完整代码

步骤1:定义 Profile 化的数据源配置类

package com.feixiang.student.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.jdbc.DataSourceBuilder;
import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {
    
    /**
     * dev 环境:H2 内存数据库
     * 本地开发开箱即用,无需安装 MySQL
     */
    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        System.out.println("[Profile: dev] 初始化 H2 内存数据库");
        return DataSourceBuilder.create()
            .driverClassName("org.h2.Driver")
            .url("jdbc:h2:mem:student_db;DB_CLOSE_DELAY=-1;MODE=MySQL")
            .username("sa")
            .password("")
            .build();
    }
    
    /**
     * test 环境:MySQL 测试库
     * QA 团队使用,数据可持久化
     */
    @Bean
    @Profile("test")
    public DataSource testDataSource() {
        System.out.println("[Profile: test] 初始化 MySQL 测试数据库");
        return DataSourceBuilder.create()
            .driverClassName("com.mysql.cj.jdbc.Driver")
            .url("jdbc:mysql://test-db:3306/student_db?useSSL=false&serverTimezone=Asia/Shanghai")
            .username("test_user")
            .password("test_pass")
            .build();
    }
    
    /**
     * prod 环境:MySQL 生产库
     * 线上环境,使用连接池优化
     */
    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        System.out.println("[Profile: prod] 初始化 MySQL 生产数据库");
        return DataSourceBuilder.create()
            .driverClassName("com.mysql.cj.jdbc.Driver")
            .url("jdbc:mysql://prod-db:3306/student_db?useSSL=true&serverTimezone=Asia/Shanghai")
            .username("prod_user")
            .password("prod_secure_pass")
            .build();
    }
}

步骤2:公共配置 application.yml

# application.yml(公共配置,所有环境共享)
server:
  port: 8080

spring:
  application:
    name: student-system
  profiles:
    active: dev  # 默认激活 dev

feixiang:
  student:
    system-name: "飞翔科技学生成绩管理系统"
    version: "2.7.18"

步骤3:环境特有配置 application-dev.yml

# application-dev.yml(dev 环境特有配置)
spring:
  datasource:
    url: jdbc:h2:mem:student_db;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver
    username: sa
    password:

feixiang:
  student:
    debug-mode: true
    log-level: DEBUG

步骤4:不同环境启动方式

# 开发环境(默认,使用 application.yml 中的 active: dev)
java -jar student-system.jar

# 测试环境(命令行激活 test)
java -jar student-system.jar --spring.profiles.active=test

# 生产环境(JVM 参数激活 prod)
java -Dspring.profiles.active=prod -jar student-system.jar

# 多 Profile 同时激活(如 dev + localcache)
java -jar student-system.jar --spring.profiles.active=dev,localcache

操作后运行结果及分析

dev 环境启动日志:

[Profile: dev] 初始化 H2 内存数据库
...
Tomcat started on port(s): 8080 (http)
Started StudentApplication in 2.345 seconds

test 环境启动日志:

[Profile: test] 初始化 MySQL 测试数据库
...
Tomcat started on port(s): 8080 (http)
Started StudentApplication in 4.567 seconds

prod 环境启动日志:

[Profile: prod] 初始化 MySQL 生产数据库
...
Tomcat started on port(s): 8080 (http)
Started StudentApplication in 5.123 seconds

分析:

  1. 条件化 Bean 注册:@Profile("dev") 确保 dev 环境只加载 H2 数据源,不会意外连接到 MySQL 生产库
  2. 默认安全:application.yml 中默认 spring.profiles.active: dev,本地开发开箱即用
  3. 生产强制切换:生产部署脚本必须显式传入 --spring.profiles.active=prod,避免默认 dev 配置泄露到生产
  4. 多 Profile 支持:可以同时激活多个 Profile,如 dev,localcache,实现更细粒度的组合配置

易错场景与面试考点

易错场景一:未激活任何 Profile 导致 Bean 缺失

小崔给测试环境写了一个 MockSmsService,但忘记标注 @Profile:

// 错误示范:未标注 @Profile,所有环境都生效
@Configuration
public class MockConfig {
    
    @Bean
    public SmsService mockSmsService() {
        return new MockSmsService();  // 模拟发送,不花真钱
    }
}

// 生产环境也有真正的 SmsService
@Configuration
public class RealConfig {
    
    @Bean
    public SmsService realSmsService() {
        return new AliyunSmsService();  // 真实发送短信
    }
}

后果:生产环境启动时,容器中同时存在 mockSmsService 和 realSmsService 两个同类型的 Bean。如果注入点使用 @Autowired,Spring 会抛出 NoUniqueBeanDefinitionException;如果 @Primary 在 mock 上,生产环境会错误地使用模拟服务发送不了短信。

正确做法:Mock 服务必须只在 dev/test 环境生效,真实服务只在 prod 生效:

@Bean
@Profile({"dev", "test"})  // 只在开发和测试环境生效
public SmsService mockSmsService() {
    return new MockSmsService();
}

@Bean
@Profile("prod")  // 只在生产环境生效
public SmsService realSmsService() {
    return new AliyunSmsService();
}

易错场景二:Profile 名称拼写错误导致默认配置生效

小崔在启动测试环境时拼错了 Profile 名:

# 错误示范:Profile 名拼写错误
java -jar student-system.jar --spring.profiles.active=tset  # ← test 拼成了 tset

后果:Spring Boot 找不到名为 tset 的 Profile,所以没有加载 application-tset.yml 和 application-tset.properties。应用使用 application.yml 中的默认配置(dev 的 H2 数据库),测试环境以为自己在连 MySQL,实际上在连 H2,测试数据不持久化,QA 团队报告"数据丢了"。

正确做法:

  • 在 application.yml 中设置 spring.profiles.default: dev 作为兜底
  • 启动脚本使用常量定义 Profile 名,避免手误
  • 在启动日志中打印当前激活的 Profile:Active Profiles: [test]

面试考点

Q:Spring Boot 的 Profile 是什么?解决了什么问题?

Profile 是 Spring 的环境隔离机制,允许为不同运行环境(dev / test / prod)定义独立的 Bean 和配置。它解决了"同一份代码在不同环境需要不同配置"的问题,避免配置混杂和误部署。

Q:激活 Profile 有哪些方式?优先级如何?

三种方式:1. 命令行参数 --spring.profiles.active=prod(最高,临时覆盖);2. application.yml 中的 spring.profiles.active(中,默认配置);3. JVM 参数 -Dspring.profiles.active=test(高,启动脚本固定)。优先级:命令行 > JVM 参数 > 配置文件。

Q:@Profile("!prod") 是什么意思?

表示"在 prod 环境不激活时生效"。常用于定义一个 Bean 在除生产环境外的所有环境都加载。例如 Mock 服务可以在 !prod 时生效,确保生产环境一定使用真实服务。

Q:Spring Boot 的 Profile 和 Maven 的 Profile 有什么区别?

Spring Boot Profile 控制运行时的 Bean 和配置加载;Maven Profile 控制编译时的依赖和构建行为。二者完全独立,但常配合使用:Maven 的 prod Profile 可以打包时排除 dev 工具,Spring Boot 的 prod Profile 控制运行时配置。

Q:多个 Profile 同时激活时,配置如何合并?

多个 Profile 按激活顺序加载,后加载的 Profile 配置覆盖先加载的同名配置。例如 --spring.profiles.active=dev,prod 时,先加载 application-dev.yml,再加载 application-prod.yml,prod 的配置会覆盖 dev 的同名配置。

上一页
章节导读:Profile与环境切换
下一页
多环境配置文件