Maven 只会引依赖?这 9 个 Profile 企业级神仙用法,让你的构建效率提升 10 倍!
你是否经历过:上线前手动改数据库地址?Windows 写好的 Shell 脚本推到 Linux 构建就报错?本地构建几秒钟,加上代码扫描就要等几分钟?
今天我们将深度拆解 Maven Profile 的 9 大企业级应用场景。从基础的环境隔离,到高阶的包形态切换、跨平台钩子、插件级差异化,代码全部开源,建议收藏!

基础篇:环境与变量管理
场景 1:多环境配置零失误 (Dev / Test / Prod)
这是 Profile 最经典的应用。坚决杜绝在代码里写死 IP 地址!
配置思路:
不要直接在 pom.xml 里写死配置,而是通过 Profile 激活特定的资源文件目录。
<profiles>
<profile>
<id>dev</id>
<activation><activeByDefault>true</activeByDefault></activation>
<properties><env>dev</env></properties>
</profile>
<profile>
<id>prod</id>
<properties><env>prod</env></properties>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes><exclude>application-*.properties</exclude></excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes><include>application-${env}.properties</include></includes>
</resource>
</resources>
</build>
效果:mvn package -Pprod,生产配置自动生效,测试配置绝不泄露。
场景 2:配置文件的“动态变量替换” (@code@ vs $)
痛点分析:
有时候我们不想维护 application-dev.yml、application-prod.yml 等一堆文件,或者有些敏感信息(如构建时间、Git版本号、数据库账号)需要由 Maven 在打包时动态注入到配置文件中。
核心机制:
利用 Maven 的 <filtering>true</filtering> 开启资源过滤,Maven 会在打包时扫描配置文件,将占位符替换为 Profile 中定义的 <properties> 值。
1. POM 配置 (开启过滤与定义变量)
首先,在 Profile 中定义差异化变量,并务必在 <build> 中开启资源过滤:
<profiles>
<profile>
<id>dev</id>
<activation><activeByDefault>true</activeByDefault></activation>
<properties>
<!-- 自定义变量 -->
<db.url>jdbc:mysql://127.0.0.1:3306/dev_db</db.url>
<log.level>DEBUG</log.level>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<db.url>jdbc:mysql://192.168.0.100:3306/prod_db</db.url>
<log.level>INFO</log.level>
</properties>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- ⚠️ 必须开启 filtering,否则变量无法替换 -->
<filtering>true</filtering>
</resource>
</resources>
</build>
2. 在 .properties 文件中使用 (标准写法)
对于传统的 .properties 文件,直接使用 Maven 标准的 ${variable} 语法即可。
文件:src/main/resources/db.properties
# Maven 打包时会自动替换 ${db.url}
jdbc.connection.url= ${db.url}
log.level=${log.level}
app.version=${project.version}
build.time=${maven.build.timestamp}
3. 在 .yaml / .yml 文件中使用 (Spring Boot 专用)
⚠️ 巨坑预警:
在 Spring Boot 项目中,application.yml 也会使用 ${...} 来读取 Spring 配置。如果 Maven 也用 ${...},两者会冲突,导致报错或替换失败。
企业级解法:
使用 Spring Boot 推荐的 @...@ 语法包裹变量名。
文件:src/main/resources/application.yml
spring:
datasource:
# 使用 @ 包裹,Maven 会识别并替换
url: @db.url@
username: admin
logging:
level:
root: @log.level@
info:
# 注入项目信息
app-name: ${project.artifactId} # 这里Maven也能解析标准变量,但在yml中推荐统一用@
version: @project.version@
效果演示
假设执行命令:
mvn clean package -Pprod
1. 生成的 db.properties 内容:
jdbc.connection.url=jdbc:mysql://192.168.0.100:3306/prod_db
app.version=1.0.0-SNAPSHOT
...
2. 生成的 application.yml 内容:
spring:
datasource:
url: jdbc:mysql://192.168.0.100:3306/prod_db
...
logging:
level:
root: INFO
形态篇:交付产物控制
场景 3:可执行包形态切换 (Jar / Fat-Jar / War)
示例A: jar/fat-jar 多种打包方式
同一个项目,有时候需要作为依赖库(Lib-Jar,瘦包)被引用,有时候需要独立部署(Fat-Jar,全量包)。
以下配置演示了如何通过 Profile 配合 maven-shade-plugin 和 maven-dependency-plugin 实现无缝切换。
<profiles>
<!-- 模式一:打胖包 (包含所有依赖的单文件) -->
<profile>
<id>fat-jar</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<finalName>encode-fat</finalName>
<transformers>
<!-- 自动在 MANIFEST.MF 中生成 Main-Class -->
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.alianga.tools.Main</mainClass>
</transformer>
</transformers>
<!-- 关键:解决依赖冲突,排除签名文件 -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!-- 模式二:打瘦包 (依赖分离到 lib 目录) -->
<profile>
<id>lib-jar</id>
<build>
<finalName>encode</finalName>
<plugins>
<!-- 1. 复制依赖到 lib -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals><goal>copy-dependencies</goal></goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
<!-- 2. 修改 Manifest Class-Path -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<finalName>encode</finalName>
<archive>
<manifest>
<mainClass>com.alianga.tools.Main</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<useUniqueVersions>false</useUniqueVersions>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
示例 B:Jar vs War (通过属性控制)
注意:虽然 Maven 支持用属性控制
<properties>
<packaging.type>jar</packaging.type> <!-- 默认值 -->
</properties>
<packaging>${packaging.type}</packaging> <!-- 动态引用 -->
<profiles>
<profile>
<id>deploy-war</id>
<properties>
<packaging.type>war</packaging.type>
</properties>
<dependencies>
<!-- 打 War 包时需要移除内置 Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
</profiles>
场景 4:差异化构建 (社区版 vs 企业版)
软件产品常有“社区版”和“企业版”之分。
企业级做法:利用 Profile 控制 <modules> 聚合,一次构建产出不同功能的包。
<profiles>
<profile>
<id>community</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>core-engine</module>
<module>web-ui</module>
</modules>
</profile>
<profile>
<id>enterprise</id>
<modules>
<module>core-engine</module>
<module>web-ui</module>
<module>security-audit-plugin</module> <!-- 企业版独有模块 -->
<module>high-availability-cluster</module> <!-- 企业版独有模块 -->
</modules>
<dependencies>
<!-- 企业版引入加密狗或License验证库 -->
<dependency>
<groupId>com.mycorp</groupId>
<artifactId>license-validator</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
</profile>
</profiles>
- 效果:开发人员平时用社区版开发核心功能,构建服务器执行
mvn clean install -Penterprise;发布时,构建服务器执行mvn clean deploy -Penterprise即可产出包含所有高级功能的全量包。
场景 5:云原生构建 (Docker 镜像)
在微服务架构中,有时候只需要打包 Jar 文件,有时候需要直接构建并推送 Docker 镜像。
企业级做法:
将 Docker 构建插件(如 docker-maven-plugin 或 jib-maven-plugin)放入特定的 Profile 中。[1]
配置示例:
<profiles>
<profile>
<id>docker-build</id>
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<to>
<image>myregistry.com/my-app:${project.version}</image>
</to>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
效果:
- 开发本地调试:mvn package (只生成 Jar,速度快)。
- CI流水线构建镜像:mvn package -Pdocker-build (生成 Jar 并推送到私有镜像仓库)。
兼容篇:跨平台与架构适配
场景 6:跨平台/架构兼容 (Windows vs Linux/Mac)
痛点:团队里有人用 Windows,CI 服务器是 Linux。如果要在构建时执行 Shell 脚本,脚本格式(.bat vs .sh)不通会导致报错。
解法:利用 OS 探测功能,自动执行对应脚本。
示例A:
生成git 提交记录日志文件,不同操作系统,脚本语法不一致
POM 配置:
<profiles>
<profile>
<id>linux-or-mac</id>
<activation>
<os><family>unix</family></os>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>clean</phase>
<goals><goal>exec</goal></goals>
<configuration>
<executable>${project.basedir}/git-log.sh</executable>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>windows</id>
<activation>
<os><family>windows</family></os>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>clean</phase>
<goals><goal>exec</goal></goals>
<configuration>
<executable>${project.basedir}/git-log.bat</executable>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
配套脚本(根目录):
1. git-log.bat (Windows)
@echo off
@chcp 65001
set "firstLine=^版本号,作者,日期,提交信息"
echo %firstLine% > git.log
git --no-pager log --date=iso --pretty="format:%%H , %%an , %%ad , %%s %%N" >> git.log
echo Git 日志已输出到 %LogFile% 文件
2. git-log.sh (Linux/Mac)
#!/bin/bash
cur_dir=$(cd "$(dirname "$0")"; pwd)
cd $cur_dir
echo "当前所在目录: $cur_dir"
currentBranch=$(git rev-parse --abbrev-ref HEAD)
echo "当前分支: $currentBranch" > git.log
firstLine="版本号,作者,日期,提交信息"
echo "$firstLine" >> git.log
git --no-pager log --date=iso --pretty=format:"%H , %an , %ad , %s %N" >> git.log
echo "Git 日志已输出到 $(pwd)/git.log 文件"
重要提示:权限管理
在 Linux 或 Mac 环境下,Maven 插件调用 Shell 脚本时,可能会因为文件没有“可执行权限(x)”而报错 Permission denied。
解决方案:
-
本地开发时:手动授权
chmod +x git-log.sh -
Git 提交时:保留权限位(推荐)
Git 会记录文件的权限位。如果你在 Windows 上提交 .sh 文件,Git 可能会丢失执行权限。建议使用以下命令强制将文件在 Git 索引中标记为可执行:git update-index --chmod=+x git-log.sh这样其他同事拉取代码或 CI 服务器构建时,该文件自动拥有执行权限。
示例B:
有些项目依赖本地库(JNI/JNA)或特定平台的二进制工具(如 Protobuf 编译器、Docker 客户端)。开发人员可能使用 Windows 或 Mac,但生产环境是 Linux。
企业级做法:
使用 Profile 的 activation 自动检测操作系统,加载对应的依赖或指定工具路径。
配置示例:
<profiles>
<profile>
<id>win-env</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<protoc.path>C:\tools\protoc.exe</protoc.path>
<native.lib.classifier>win-x64</native.lib.classifier>
</properties>
</profile>
<profile>
<id>linux-env</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<properties>
<protoc.path>/usr/bin/protoc</protoc.path>
<native.lib.classifier>linux-x64</native.lib.classifier>
</properties>
</profile>
</profiles>
<dependencies>
<!-- 根据 OS 自动加载对应的本地库 -->
<dependency>
<groupId>org.rocksdb</groupId>
<artifactId>rocksdbjni</artifactId>
<version>6.29.4</version>
<classifier>${native.lib.classifier}</classifier>
</dependency>
</dependencies>
- 效果:Windows 同事和使用 Linux 的 Jenkins 都不需要修改任何配置,Maven 自动识别操作系统并下载对应的依赖包。
场景 7:依赖差异化 (JDK / 中间件版本)
场景:公司做架构升级,需要同时维护 JDK 8 和 JDK 17 版本,或者同时适配 Spark 2.4 和 3.0。
<properties>
<!-- 默认配置 (例如 JDK 8 + Spark 2.4) -->
<java.version>1.8</java.version>
<spark.version>2.4.8</spark.version>
<scala.binary.version>2.11</scala.binary.version>
</properties>
<profiles>
<!-- 升级版配置 (JDK 17 + Spark 3.2) -->
<profile>
<id>jdk17</id>
<properties>
<java.version>17</java.version>
<spark.version>3.2.1</spark.version>
<scala.binary.version>2.12</scala.binary.version>
</properties>
<dependencies>
<!-- 仅在 JDK 17 下需要的额外模块(如 javax 替换包) -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
质量与流水线篇
场景 8:CI/CD 流水线优化 (跳过繁重任务)
-
在 CI/CD 流水线中,不同的阶段关注点不同。
- 提交构建(Commit Build):要求快,做简单的单元测试。
- 夜间构建(Nightly Build):可以慢,需要做全量集成测试、静态代码扫描(Sonar)、生成站点文档。
- 发布构建(Release Build):需要做 GPG 签名、生成 Source Jar 和 Javadoc。
企业级做法:
通过 Profile 隔离耗时插件,避免每次本地构建都运行耗时任务。配置示例:
<profiles>
<!-- CI 环境专用 Profile -->
<profile>
<id>ci-check</id>
<build>
<plugins>
<!-- 仅在 CI 环境运行静态代码检查 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
<!-- 仅在 CI 环境运行集成测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals><goal>integration-test</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!-- 发布专用 Profile -->
<profile>
<id>release</id>
<build>
<plugins>
<!-- 发布时自动进行 GPG 签名 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<phase>verify</phase>
<goals><goal>sign</goal></goals>
</execution>
</executions>
</plugin>
<!-- 发布时生成 JavaDoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals><goal>jar</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
场景 9:插件级差异 (开发飞快 vs 生产合规)
企业最容易忽略的高级用法:
痛点:OWASP Dependency Check 或 SpotBugs 运行非常慢(可能需要几分钟)。如果绑定在默认生命周期中,开发人员每次 mvn install 都要等很久,严重影响效率。但如果不加,预发布或生产环境又存在安全隐患。
企业级做法:
开发环境(Dev)跳过重型检查,CI/CD 流水线的特定阶段(如 Release 或 Nightly Check)强制开启并配置“失败中断”。
<profiles>
<!-- 严格检查模式:通常在 CI 服务器上激活 -->
<profile>
<id>strict-audit</id>
<build>
<plugins>
<!-- 1. 代码风格检查 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<goals><goal>check</goal></goals>
<configuration>
<configLocation>company-checkstyle.xml</configLocation>
<failOnViolation>true</failOnViolation> <!-- 违规即构建失败 -->
</configuration>
</execution>
</executions>
</plugin>
<!-- 2. 静态潜在 Bug 扫描 (SpotBugs) -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<executions>
<execution>
<phase>verify</phase>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
<!-- 3. 第三方依赖漏洞扫描 (OWASP - 极慢,必须隔离) -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<executions>
<execution>
<phase>verify</phase>
<goals><goal>check</goal></goals>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS> <!-- CVSS 分数大于7(高危)则构建失败 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
- 用法:开发平时运行 mvn clean install (飞快);CI 流水线运行
mvn clean install -Pstrict-audit(严格把关)。
总结
Maven Profile 不仅仅是简单的配置切换,它是工程化、标准化、自动化的基石。
通过上述 9 大场景,我们实现了:
- 环境隔离:开发、测试、生产互不干扰。
- 形态控制:灵活产出 Fat-Jar、Lib-Jar 或 Docker 镜像。
- 流程分级:平衡本地开发的“快”与线上发布的“稳”。
- 平台兼容:抹平 Windows 与 Linux 的脚本差异。
掌握这些技巧,你的 pom.xml 就不再是简单的依赖列表,而是强大的构建脚本。
整理不易,如果觉得有用,点个推荐 和 喜欢支持一下!
Q.E.D.


