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

    • 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章 仓库管理深入

    • 仓库组与路由

Maven与持续集成

本章是 Maven 进阶教程的工程化落地章节。前面章节讲清了 Maven 的构建机制、依赖管理和多模块组织,本章回答的问题是:如何让这些能力在自动化流水线中稳定、可复现地运行。理解 Maven 与 CI/CD 的集成方式,是将"本地能跑"升级为"线上必稳"的关键一步。


核心机制

持续集成(Continuous Integration,CI)的核心思想是:代码提交后,自动触发构建、测试和反馈。Maven 在这个流程中扮演"标准化构建引擎"的角色——它用同一套 pom.xml 和生命周期,确保开发机、测试机、生产机的构建行为完全一致。

Maven 在 CI/CD 中的独特价值

为什么 CI/CD 流水线偏爱 Maven,而不是让开发者自己写 Shell 脚本编译?

维度手写 Shell 脚本Maven 标准化构建
依赖管理手动下载 JAR,脚本里写死路径自动从仓库解析,版本锁定在 pom.xml
构建步骤javac → cp → jar 命令拼接,顺序靠人记生命周期阶段固定,顺序由 Maven 保证
跨平台Windows 批处理和 Linux Shell 不兼容mvn 命令在任何操作系统行为一致
可复现性换台机器可能因环境差异失败只要 JDK 和 Maven 版本一致,结果必然一致
插件生态自己实现代码检查、测试报告生成数千个插件开箱即用,一行配置即可接入

Maven 的价值在于把"构建"从"手艺活"变成"标准工序"。流水线不关心你的项目用 Spring 还是 Quarkus,只要看到 pom.xml,就知道该执行 mvn clean test package。

典型流水线阶段

一条完整的 Maven CI/CD 流水线通常包含五个阶段:

  1. 检出代码:git pull 或 git clone
  2. 编译与单元测试:mvn clean test —— 验证代码能否编译、测试是否通过
  3. 打包:mvn package -DskipTests —— 生成可部署产物(JAR/WAR)
  4. 质量检查(可选):mvn verify —— 触发集成测试、代码覆盖率、静态分析
  5. 部署:将 target/ 中的产物推送到服务器或镜像仓库

生活类比:中央厨房的"标准化作业书"

想象飞翔科技开了一家连锁餐厅:

  • 没有 Maven 的 CI/CD:每家分店(开发/测试/生产环境)的厨师(运维)自己决定怎么炒菜(构建),有的先放盐有的后放盐,顾客(用户)吃到的味道(产物)各不相同。
  • 有 Maven 的 CI/CD:总部(Maven)制定了一本《标准化作业书》(pom.xml),规定每道菜的食材(依赖)、火候(插件参数)、工序(生命周期)。无论哪家分店,只要按作业书操作,出品完全一致。中央厨房(仓库)统一配送食材,分店不需要自己去菜市场。

图示

上图展示了一条标准的 Maven CI/CD 流水线。关键设计是"质量门禁":mvn clean test 是第一道门,单元测试失败就阻断后续流程;mvn verify 是第二道门,集成测试或代码覆盖率不达标同样阻断。这种"早失败、快反馈"的机制,避免有问题的代码流向生产环境。


完整示例

场景

飞翔科技的 order-service(订单微服务)项目进入快速迭代期。CTO 大翔要求:每次代码提交后,必须自动编译、测试、打包,测试失败立即通知团队,不能靠人工手动打包上传。架构师白歌负责设计 CI/CD 流水线,后端小崔配合调整 Maven 配置,运维李眉搭建 Jenkins 环境,前端黄俪的 Node 项目也准备参照同一套模式。

Jenkinsfile 流水线配置

白歌在订单服务仓库根目录创建了 Jenkinsfile,定义完整的 Maven 流水线:

pipeline {
    agent any

    tools {
        maven 'Maven-3.9.6'
        jdk 'JDK-17'
    }

    stages {
        stage('检出代码') {
            steps {
                git branch: 'main', url: 'https://git.feixiang.tech/order-service.git'
            }
        }

        stage('编译与单元测试') {
            steps {
                sh 'mvn clean test'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }

        stage('打包') {
            steps {
                sh 'mvn package -DskipTests'
            }
        }

        stage('质量门禁') {
            steps {
                sh 'mvn verify'
            }
        }

        stage('部署到测试环境') {
            when {
                branch 'develop'
            }
            steps {
                sh '''
                    scp target/order-service-*.jar deploy@staging:/opt/apps/
                    ssh deploy@staging "systemctl restart order-service"
                '''
            }
        }

        stage('部署到生产环境') {
            when {
                branch 'main'
            }
            steps {
                sh '''
                    scp target/order-service-*.jar deploy@prod:/opt/apps/
                    ssh deploy@prod "systemctl restart order-service"
                '''
            }
        }
    }

    post {
        failure {
            mail to: 'team@feixiang.tech',
                 subject: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                 body: "请检查 ${env.BUILD_URL}"
        }
    }
}

各阶段 Maven 命令详解

阶段命令作用产物位置
编译与测试mvn clean test清理旧产物,编译代码,运行单元测试target/test-classes/、target/surefire-reports/
打包mvn package -DskipTests将编译结果打成 JAR/WAR,跳过已跑过的测试target/order-service-1.0.0.jar
质量验证mvn verify运行集成测试、代码检查、覆盖率统计target/failsafe-reports/、target/site/
安装到本地mvn install将产物放入本地仓库,供其他模块引用~/.m2/repository/com/feixiang/order-service/1.0.0/
发布到远程mvn deploy将产物上传到私服,供全公司使用公司 Nexus 仓库

小崔的调整:为 CI 优化 pom.xml

为了让流水线更稳定,小崔在 pom.xml 中做了三处调整:

<project>
    ...
    <properties>
        <!-- 1. 锁定编码,避免不同操作系统默认编码差异导致编译失败 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <!-- 2. 显式声明 JDK 版本,与流水线工具链保持一致 -->
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <!-- 3. 配置 Surefire 插件,确保测试失败时构建立即终止 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.1.2</version>
                <configuration>
                    <failIfNoTests>true</failIfNoTests>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

变化分析:

  • 李眉在 Jenkins 里配置好 Maven 和 JDK 后,任何开发者提交代码都会自动触发相同流程
  • 黄俪的前端项目虽然用 npm,但流水线结构(检出 → 构建 → 测试 → 部署)完全一致,团队容易理解
  • 大翔在 Jenkins 仪表盘上看到每次构建的状态,测试失败时邮件自动通知责任人

易错点与常见问题

误区一:CI 里用 mvn install 代替 mvn test

错误做法:流水线第一步就执行 mvn clean install。

问题:install 会把产物写入本地仓库(~/.m2/repository),如果测试失败,有问题的 JAR 已经被安装,可能被其他构建误引用。而且 install 包含 package、verify 等后续阶段,测试失败时反馈慢——你要等整个生命周期跑完才知道有问题。

正确做法:CI 的第一道验证应该是 mvn clean test。测试通过后,再执行 mvn package 或 mvn verify。只有需要把产物提供给其他项目时,才在流水线末端执行 mvn deploy。

误区二:-DskipTests 和 -Dmaven.test.skip=true 混用

错误认知:"跳过测试嘛,两个参数随便用。"

纠正:两者有本质区别:

参数效果适用场景
-DskipTests不运行测试,但仍然编译测试代码测试已通过,只想快速打包
-Dmaven.test.skip=true不运行测试,也不编译测试代码测试代码有编译错误,临时跳过

在 CI 流水线中,打包阶段用 -DskipTests 是合理的(因为前面的 test 阶段已经验证过),但如果在本地开发时长期用 -Dmaven.test.skip=true 逃避测试,测试代码会逐渐腐烂,等到 CI 里跑测试时集中爆发。

误区三:流水线不固定 Maven 和 JDK 版本

错误做法:Jenkins 的 agent 使用服务器默认的 mvn 命令,版本随环境漂移。

问题:小崔本地用 Maven 3.9.6 + JDK 17,李眉的 Jenkins 节点 A 装的是 Maven 3.6.3 + JDK 11,节点 B 又是另一套版本。同样的 pom.xml,在不同节点上行为可能不同(比如旧版 Maven 对某些插件版本解析有差异),导致"在我机器上能跑"的诡异故障。

正确做法:在 Jenkins 的 Global Tool Configuration 中注册固定版本的 Maven 和 JDK,在 Jenkinsfile 中通过 tools 块显式引用。或者更好的方式,使用 Docker 镜像作为构建环境,把 Maven、JDK 和项目依赖全部封装在一个可控的容器里。


小结

Maven 是 CI/CD 流水线的理想构建引擎,因为它用 pom.xml 提供了跨平台、可复现、插件化的标准构建能力。一条典型的 Maven 流水线遵循"检出 → 编译测试 → 打包 → 质量验证 → 部署"的节奏,通过 mvn clean test 作为第一道质量门禁,确保有问题的代码不会流向后续环节。在 CI 环境中,务必锁定 Maven 和 JDK 版本、显式声明编码和编译参数,避免环境漂移带来的不可复现故障。

本章与全局的关系:本章回答了"如何让 Maven 构建在自动化环境中稳定运行"。下一章将深入 Maven 的插件开发机制,讲解如何编写自定义插件来扩展 Maven 的能力,满足团队特有的构建需求。