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

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

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

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 多模块项目深入

    • Reactor构建顺序
    • 继承与聚合组合实践
    • 模块间依赖
  • 第2章 插件体系深入

    • 插件目标goal
    • execution与自定义绑定
    • pluginManagement
  • 第3章 Profile高级应用

    • Profile激活机制
    • Profile与Spring Profile区别
  • 第4章 部署与分发

    • distributionManagement
    • mvn deploy
    • settings.xml认证配置
  • 第5章 CI/CD集成

    • Maven与持续集成
  • 第6章 自定义插件开发

    • 自定义插件开发
  • 第7章 高级依赖管理

    • 快照版本机制
    • 依赖分析工具
  • 第8章 仓库管理深入

    • 仓库组与路由

Profile与Spring Profile区别

本章是Profile主题的延伸。许多开发者同时接触Maven Profile和Spring Profile,容易混淆两者的作用域和时机。厘清"构建期"与"运行期"的分工,是避免环境配置错位的关键。


核心机制

Maven Profile和Spring Profile名字相似,但解决的问题完全不同。把它们混为一谈,就像把"建筑图纸"和"室内装修"当成一回事——前者决定房子怎么盖,后者决定住进去用什么家具。

作用域与时机的根本差异

维度Maven ProfileSpring Profile
控制时机构建期(Build Time)运行期(Runtime)
控制对象Maven构建过程:依赖、插件、资源过滤、编译参数Spring应用上下文:Bean的加载、配置类的选择、属性源的切换
生效位置Maven引擎内部,在打包之前JVM内部,在应用启动之后
典型用途不同环境打不同的包(如prod包排除dev工具)同一套包在不同环境加载不同的Bean
配置位置pom.xml或settings.xmlapplication.yml、@Profile注解、@Conditional
传递性不进入运行时,构建完即消失贯穿整个应用生命周期

一句话总结

  • Maven Profile:控制"打包时放什么进去"
  • Spring Profile:控制"运行时从包里拿出什么来用"

两者如何协作

在实际项目中,两者不是互斥的,而是接力关系:

  1. Maven Profile在构建期选择资源文件、过滤属性值、决定依赖范围
  2. 打包完成后,Maven Profile的使命结束
  3. 应用启动时,Spring Profile读取jar包内的application.yml,决定激活哪套Bean配置
  4. 如果Maven Profile在打包时把application-prod.yml放进了jar,Spring Profile才能在运行时发现它

生活类比:餐厅后厨与前台服务

想象飞翔科技大楼的餐厅:

  • Maven Profile是后厨备菜:根据今天是"商务宴请"还是"员工午餐",决定采购什么食材、准备什么菜品。菜做好后,后厨的工作就结束了。
  • Spring Profile是前台服务:客人入座后,服务员根据"VIP包厢"或"普通散座"的标签,决定上哪套餐具、提供哪份菜单。客人吃什么,取决于后厨做了什么菜,但上什么菜由前台根据座位标签决定。

如果后厨没做"佛跳墙"(Maven Profile没把生产配置打包进去),前台就算给客人贴了"VIP"标签(Spring Profile激活了prod),也端不出这道菜。


图示

上图展示了Maven Profile与Spring Profile的接力协作关系。构建期Maven决定jar包里有什么,运行期Spring决定从jar包里用什么。中间的唯一纽带是jar包本身——如果Maven没把某份配置打进去,Spring运行时无论如何也找不到。


完整示例

场景

飞翔科技的order-service要同时支持Maven和Spring的多环境能力。CTO大翔的要求是:

  1. 构建期:生产包必须排除开发工具(如spring-boot-devtools),减小体积
  2. 运行期:同一套jar包,通过启动参数切换连测试库还是生产库

架构师白歌设计了两层Profile体系。

第一层:Maven Profile(构建期)

<?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>

    <groupId>com.feixiang</groupId>
    <artifactId>order-service</artifactId>
    <version>1.2.0</version>

    <profiles>
        <!-- 开发构建:包含devtools,方便热部署 -->
        <profile>
            <id>dev-build</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-devtools</artifactId>
                    <scope>runtime</scope>
                </dependency>
            </dependencies>
            <build>
                <resources>
                    <resource>
                        <directory>src/main/resources</directory>
                        <filtering>true</filtering>
                        <excludes>
                            <exclude>application-prod.yml</exclude>
                        </excludes>
                    </resource>
                </resources>
            </build>
        </profile>

        <!-- 生产构建:排除devtools,打包prod配置 -->
        <profile>
            <id>prod-build</id>
            <build>
                <resources>
                    <resource>
                        <directory>src/main/resources</directory>
                        <filtering>true</filtering>
                        <excludes>
                            <exclude>application-dev.yml</exclude>
                        </excludes>
                    </resource>
                </resources>
            </build>
        </profile>
    </profiles>
</project>

第二层:Spring Profile(运行期)

src/main/resources/application.yml(主配置,不打环境具体值):

spring:
  profiles:
    active: ${SPRING_PROFILES_ACTIVE:dev}
  application:
    name: order-service

src/main/resources/application-dev.yml:

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/order_dev
    username: root
    password: dev123

src/main/resources/application-prod.yml:

server:
  port: 80
spring:
  datasource:
    url: jdbc:mysql://prod-cluster.feixiang.com:3306/order_prod
    username: prod_app
    password: ${DB_PASSWORD}

各角色的实际工作流

小崔(后端开发)的日常:

# 1. 用Maven dev-build打包(默认激活,包含devtools)
mvn clean package

# 2. 启动时Spring Profile用dev(默认)
java -jar target/order-service-1.2.0.jar
# 效果:端口8080,连本地库,有热部署

李眉(运维)的生产部署:

# 1. 用Maven prod-build打包(排除devtools,只保留prod配置)
mvn clean package -Pprod-build

# 2. 启动时显式指定Spring Profile为prod
java -jar -Dspring.profiles.active=prod \
          -DDB_PASSWORD=xxx \
          target/order-service-1.2.0.jar
# 效果:端口80,连生产集群,无devtools

黄俪(前端联调)的测试环境:

# 黄俪用prod-build的包(和线上同构),但启动时切到dev配置
# 注意:这要求prod-build没有排除application-dev.yml,或者使用外部配置
java -jar -Dspring.profiles.active=dev \
          -Dspring.config.location=classpath:/,file:/opt/feixiang/dev-config/ \
          target/order-service-1.2.0.jar

易错点与常见问题

误区一:用Maven Profile替代Spring Profile

错误认知:"我在pom.xml里用Profile把application.yml的spring.datasource.url过滤成不同值,这样运行时就不用管了。"

纠正:这种做法在简单项目里能跑通,但有三个隐患:

  1. 同一jar包无法多环境复用:你打了prod包,就不能用它连测试库——jar包里的application.yml已经被写死成生产地址
  2. 敏感信息泄露:如果生产密码通过Maven过滤写进application.yml,这个密码会永远留在jar包里,任何拿到jar的人都能反编译看到
  3. 与Spring生态不兼容:Spring Cloud Config、Kubernetes ConfigMap等运行时配置机制,都假设配置在运行期可变

正确做法:Maven Profile只控制"哪些文件进包"和"构建参数",运行时配置交给Spring Profile或外部配置中心。

误区二:Spring Profile能加载Maven Profile过滤后的变量

错误认知:"我在pom.xml的Profile里定义了<db.url>xxx</db.url>,然后在application.yml里写url: @db.url@,Spring启动时能读到。"

纠正:能读到,但那是Maven的功劳,不是Spring的。@db.url@是Maven资源过滤的占位符语法,在mvn package时就被替换成了具体值。Spring看到的已经是替换后的字符串,它根本不知道db.url这个Maven属性的存在。如果你直接运行IDE里的Main方法(不走Maven打包),@db.url@不会被替换,Spring就会读到字面量@db.url@,导致启动失败。

反例:

# application.yml(未经Maven打包时)
spring:
  datasource:
    url: @db.url@

在IDEA里右键运行OrderServiceApplication:

Failed to configure DataSource: 'url' attribute is not specified
# 实际原因是Spring读到了字面量 "@db.url@",无法解析为有效URL

正确做法:如果需要在IDE里直接运行,使用${DB_URL:jdbc:mysql://localhost:3306/order_dev}这种Spring表达式,而非Maven过滤占位符。

误区三:生产包和开发包用同一个Maven Profile

错误认知:"我只有一个jar包,通过启动参数-Dspring.profiles.active=prod就能上生产,不需要单独的Maven Profile。"

纠正:开发包和生产包的内容应该不同。开发包需要:

  • spring-boot-devtools(热部署)
  • 详细的日志配置
  • 内嵌的H2数据库(可选)

生产包需要:

  • 排除devtools(减少攻击面、减小体积)
  • 精简的日志配置
  • 健康检查端点

如果你用同一个jar包,devtools会进入生产环境——它允许远程调试和自动重启,是严重的安全隐患。


小结

Maven Profile和Spring Profile是不同时空的工具:

  • Maven Profile在构建期工作,决定"包里装什么"
  • Spring Profile在运行期工作,决定"启动后用什么"

优秀的多环境架构应该让两者各司其职、接力配合:Maven负责构建差异化(如排除dev工具),Spring负责运行差异化(如切换数据源)。混淆两者的边界,会导致jar包无法复用、敏感信息泄露、或开发工具误入生产环境。

本章与全局的关系:本章厘清了构建期与运行期的Profile分工。下一章将进入"部署与分发"主题,讲解如何把构建好的jar包发布到远程仓库供团队共享。

上一页
Profile激活机制