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

    • 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章 介绍与核心概念

    • Maven是什么
    • 约定优于配置
  • 第2章 安装与配置

    • 安装与验证
    • settings.xml
    • 本地仓库与镜像
  • 第3章 POM与项目坐标

    • POM
    • GAV坐标
    • packaging
  • 第4章 标准目录布局

    • 标准目录布局
  • 第5章 依赖机制

    • dependencies
    • scope
    • 依赖传递
    • 依赖冲突与调解
    • exclusions
    • optional
    • dependencyManagement
  • 第6章 仓库

    • 仓库体系
    • 本地仓库
    • 远程仓库与镜像
    • 私服
  • 第7章 构建生命周期

    • 生命周期概述
    • clean 生命周期
    • default 生命周期
    • site 生命周期
    • 生命周期与插件绑定
  • 第8章 插件

    • 插件概述
    • maven-compiler-plugin
    • maven-surefire-plugin
    • maven-war-plugin
  • 第9章 继承与聚合

    • parent继承
    • 聚合
    • BOM
    • properties
  • 第10章 属性与资源过滤

    • 资源过滤
    • Profile
  • 第11章 常用命令

    • mvn compile
    • mvn test
    • mvn package
    • mvn clean
    • mvn install
    • mvn dependency:tree
  • 第12章 常见问题与最佳实践

    • 依赖冲突排查
    • 最佳实践

Profile

本章承接"资源过滤",是 Maven 多环境构建的核心机制。理解 Profile,才能将开发、测试、生产三套配置优雅地组织在同一份 pom.xml 中,避免"复制三份项目"的野蛮做法。


核心机制

Profile 用于针对不同环境和目标平台定制构建。它让你只需维护一份 POM,却能面向不同的运行时环境生成不同的构建产物。

这句话的潜台词是:没有 Profile,你可能需要维护 pom-dev.xml、pom-test.xml、pom-prod.xml 三份文件;有了 Profile,所有环境差异被封装在同一份 pom.xml 的不同段落中。

什么是 Profile?

Profile 是 pom.xml 中一段条件化配置。每个 Profile 有一个唯一标识(id),内部可以定义:

  • 自定义属性(<properties>)
  • 依赖列表(<dependencies>)
  • 插件配置(<plugins>)
  • 资源过滤规则(<resources>)
  • 构建输出目录(<directory>)

当某个 Profile 被激活时,它内部的配置会合并到主构建中;未被激活时,这段配置完全透明,不影响构建。

Profile 的激活方式

Maven 提供了多种激活 Profile 的手段:

激活方式配置位置使用场景
命令行激活mvn clean package -PprodCI/CD 流水线、自动化部署
默认激活<activation><activeByDefault>true</activeByDefault></activation>未指定环境时的兜底配置
属性激活<activation><property><name>env</name><value>dev</value></property></activation>根据系统属性或环境变量自动切换
JDK 版本激活<activation><jdk>17</jdk></activation>针对特定 JDK 版本调整编译参数
操作系统激活<activation><os><family>windows</family></os></activation>跨平台项目的平台差异处理

多环境配置的设计思路

飞翔科技的标准做法是:

  1. 定义三个 Profile:dev(开发)、test(测试)、prod(生产)
  2. 每个 Profile 只定义差异:数据库连接、日志级别、外部服务地址等
  3. 默认激活 dev:开发者在本地不指定 -P 时,自动使用开发环境配置
  4. CI/CD 显式指定环境:Jenkins 构建测试包时带 -Ptest,构建生产包时带 -Pprod

生活类比:多功能瑞士军刀

想象你(运维李眉)去野外工作,带了一把瑞士军刀(pom.xml)。这把刀有多个可弹出的工具(Profile):

  • 主刀(dev):日常切水果,默认弹出,最常用
  • 螺丝刀(test):修设备时用,需要手动拨到对应档位
  • 锯子(prod):砍树枝搭帐篷,只在特定场景使用

你不需要带三把独立的刀,只需要一把带多个档位的瑞士军刀。Profile 就是 POM 的"档位切换器"。


图示

上图展示了 Profile 的核心设计:同一份 pom.xml 包含多个 Profile,通过不同的激活方式选择其中一个,最终生成对应环境的构建产物。三个 Profile 互斥激活,各自只定义环境差异,公共配置放在 Profile 之外。


完整示例

场景

飞翔科技的 employee-system 项目需要部署到三个环境:

  • 开发环境(dev):开发者本地调试,数据库用本机 MySQL,日志全开
  • 测试环境(test):测试团队验证,数据库用测试服务器,日志级别 INFO
  • 生产环境(prod):线上运行,数据库用主从集群,日志级别 WARN,关闭调试接口

CTO 大翔要求:不允许维护三份代码,所有环境差异必须在同一份 pom.xml 中管理。

操作前:项目状态

src/main/resources/application.properties(模板文件,含占位符):

# 数据库配置
db.host=${db.host}
db.port=${db.port}
db.name=${db.name}
db.username=${db.username}
db.password=${db.password}

# 日志配置
log.level=${log.level}
log.path=${log.path}

# 应用配置
debug.enabled=${debug.enabled}
app.env=${app.env}

操作步骤

步骤一:在 pom.xml 中定义多 Profile

架构师白歌在 pom.xml 中配置三个 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>employee-system</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

    <profiles>
        <!-- 开发环境:默认激活 -->
        <profile>
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <db.host>localhost</db.host>
                <db.port>3306</db.port>
                <db.name>employee_dev</db.name>
                <db.username>dev_user</db.username>
                <db.password>dev_pass</db.password>
                <log.level>DEBUG</log.level>
                <log.path>/tmp/logs/employee</log.path>
                <debug.enabled>true</debug.enabled>
                <app.env>development</app.env>
            </properties>
        </profile>

        <!-- 测试环境 -->
        <profile>
            <id>test</id>
            <properties>
                <db.host>test-db.feixiang.com</db.host>
                <db.port>3306</db.port>
                <db.name>employee_test</db.name>
                <db.username>test_user</db.username>
                <db.password>${TEST_DB_PASSWORD}</db.password>
                <log.level>INFO</log.level>
                <log.path>/var/log/employee/test</log.path>
                <debug.enabled>true</debug.enabled>
                <app.env>testing</app.env>
            </properties>
        </profile>

        <!-- 生产环境 -->
        <profile>
            <id>prod</id>
            <properties>
                <db.host>prod-db.feixiang.com</db.host>
                <db.port>3306</db.port>
                <db.name>employee_prod</db.name>
                <db.username>prod_user</db.username>
                <db.password>${PROD_DB_PASSWORD}</db.password>
                <log.level>WARN</log.level>
                <log.path>/var/log/employee/prod</log.path>
                <debug.enabled>false</debug.enabled>
                <app.env>production</app.env>
            </properties>
        </profile>
    </profiles>
</project>

步骤二:不同环境的构建命令

开发环境(默认,无需 -P):

mvn clean package

测试环境:

mvn clean package -Ptest

生产环境:

mvn clean package -Pprod

不同环境的配置差异对比

配置项dev(开发)test(测试)prod(生产)
db.hostlocalhosttest-db.feixiang.comprod-db.feixiang.com
db.nameemployee_devemployee_testemployee_prod
log.levelDEBUGINFOWARN
log.path/tmp/logs/employee/var/log/employee/test/var/log/employee/prod
debug.enabledtruetruefalse
app.envdevelopmenttestingproduction
激活方式默认(不带 -P)mvn ... -Ptestmvn ... -Pprod

操作结果及分析

开发环境构建结果(target/classes/application.properties):

db.host=localhost
db.port=3306
db.name=employee_dev
...
log.level=DEBUG
debug.enabled=true
app.env=development

生产环境构建结果(target/classes/application.properties):

db.host=prod-db.feixiang.com
db.port=3306
db.name=employee_prod
...
log.level=WARN
debug.enabled=false
app.env=production

分析:

  • 小崔在本地开发时,不指定 -P,自动使用 dev 配置,连接本机数据库
  • 李眉在 Jenkins 上构建测试包时,命令带 -Ptest,自动连接测试数据库
  • 生产部署时,运维脚本带 -Pprod,且密码通过环境变量 ${PROD_DB_PASSWORD} 注入,避免硬编码敏感信息

易错点与常见问题

误区一:同时激活多个 Profile 会冲突

错误认知:"我执行 mvn clean package -Pdev,prod,两个 Profile 同时激活,属性会合并。"

纠正:Maven 允许同时激活多个 Profile(用逗号分隔),但如果两个 Profile 定义了同名属性,后激活的 Profile 会覆盖前者。更危险的是,如果两个 Profile 定义了不同的依赖版本或插件配置,合并行为可能产生难以预料的结果。最佳实践是:环境类 Profile 互斥激活,一次只激活一个。

# 错误:同时激活 dev 和 prod
mvn clean package -Pdev,prod

# 正确:只激活一个
mvn clean package -Pprod

误区二:activeByDefault 在存在其他激活条件时失效

典型问题:小崔发现明明在 dev Profile 里写了 <activeByDefault>true</activeByDefault>,但执行 mvn clean package -Ptest 时,dev 的属性没有生效。

纠正:activeByDefault 的语义是"当没有任何其他 Profile 被激活时,默认激活我"。一旦通过 -P 显式激活了其他 Profile,activeByDefault 自动失效。这是设计如此,不是 bug。

误区三:Profile 里定义的属性是全局的

错误认知:"我在 dev Profile 里定义了 <db.host>localhost</db.host>,这个属性只在 dev 激活时存在,其他时候不存在。"

纠正:这个认知基本正确,但有一个细节:Profile 激活后,其内部定义的属性确实只在本次构建中生效。但如果你在命令行用 -Ddb.host=custom 覆盖,命令行属性的优先级高于 Profile 属性。优先级顺序是:命令行属性 > Profile 属性 > POM <properties> > 外部过滤文件。

误区四:用 Profile 管理环境是"唯一正确做法"

纠正:Maven Profile 适合管理构建时差异(如编译参数、资源过滤值)。对于运行时差异(如生产环境动态切换配置),更推荐:

  • Spring Boot 的 application-{profile}.yml + 启动参数 --spring.profiles.active=prod
  • Kubernetes 的 ConfigMap / Secret 注入
  • 外部配置中心(Nacos、Apollo)

Maven Profile 解决的是"构建不同环境的包",运行时配置切换应由应用框架或基础设施负责。


小结

Profile 是 Maven 实现多环境构建的核心机制。通过在同一份 pom.xml 中定义多个 Profile,并用不同的激活方式(默认激活、命令行 -P、属性匹配等)选择当前环境,团队可以:

  • 避免维护多份项目代码
  • 将环境差异集中管理、一目了然
  • 在 CI/CD 流水线中通过参数切换环境

核心要点:

  • 环境类 Profile 建议互斥激活,一次只选一个
  • activeByDefault 只在无其他 Profile 激活时生效
  • 敏感信息(密码)通过环境变量注入,不要硬编码在 POM 中
  • Profile 适合构建时差异,运行时差异建议由应用框架处理

本章与全局的关系:本章讲解了 Profile 的多环境配置能力。结合上一章的"资源过滤",你已经掌握了"同一份代码 + 不同 Profile = 不同环境产物"的完整链条。下一章将进入"常用命令",学习如何在命令行中实际执行这些构建。

上一页
资源过滤