0. 构建工具概览

为什么要使用构建工具?

在没有构建工具的时代,开发者需要手动编译源码(javac)、管理依赖(下载 JAR 包)、打包(jar 命令)、运行测试、部署等。这些过程繁琐、易错、难以复现。

构建工具是软件开发中用于自动化构建、依赖管理、项目管理等任务的工具,用于简化构建流程,提高开发效率,确保项目的一致性和可重复性

构建工具分类

  • Maven:基于 Apache 的构建工具,使用 pom.xml 文件进行项目配置
  • Gradle:基于 Groovy 的构建工具,使用 build.gradle 文件进行项目配置,支持多种构建脚本语言(如 Kotlin、Java)

构建工具在项目中的位置

构建工具并不只是“下载依赖的工具”,它处在源码、依赖、测试、制品、部署之间,是整个工程交付链路的调度器。

flowchart TD
    A[源码<br/>src/main] --> B[构建工具<br/>Maven / Gradle]
    C[测试代码<br/>src/test] --> B
    D[远程仓库<br/>Maven Central / 私服] --> B
    E[本地仓库 / 构建缓存] --> B

    B --> F[编译<br/>javac / kotlinc]
    F --> G[测试<br/>JUnit / Kotest]
    G --> H[打包<br/>jar / war / image]
    H --> I[发布<br/>本地仓库 / 远程仓库 / CI]

    B --> J[插件系统]
    J --> K[代码生成<br/>检查<br/>容器镜像<br/>文档]

1. Maven

Maven -- 约定大于配置的典范

Maven是什么

Maven 是一种声明式项目管理工具,通过在 POM(Project Object Model) 中配置 “who”,“what”,“where”等信息,即可满足编译、测试、打包、发布等项目构建需求。 声明式的好处是,用户无需关心构建工具的实现细节,只需在 pom.xml 中配置好项目名,依赖等基础信息即可。坏处是,实现自定义的构建逻辑,相对复杂。(Maven 也提供了插件,如:maven-antrun-plugin,来运行用户自定义脚本。当然,插件最终 apply 到 Maven 的方式最终仍然是声明式的,即需要在 POM 中声明插件运行时机和插件相关配置。)

相对应地,Make 和 Ant 等构建工具是过程式项目管理工具,用户需要编写构建脚本并组织各脚本的依赖关系。过程式项目管理工具好处是,用户自由度很大;坏处是,项目管理经验无法复用,构建脚本编写较为复杂。

Maven的核心理念和项目结构

  1. 核心理念:
  • 约定大于配置:默认目录结构,生命周期固定
  • 声明式构建:声明项目信息,而非编写构建脚本
  • 中心化仓库:依赖中央仓库(Maven Central Repository)
  1. 项目结构:
my-app/ 
├── pom.xml 
├── src/  ← 源代码目录
│ ├── main/ 
│ │ ├── java/ ← 主代码 
│ │ └── resources/ ← 主资源 
│ └── test/  ← 单元测试
│ ├── java/ ← 测试代码 
│ └── resources/ ← 测试资源 
└── target/ ← 构建输出目录
  1. 核心概念:
  • 项目对象模型(POM):pom.xml 是 Maven 的核心配置文件,用于定义项目信息、依赖、插件等,包含:
    • 项目坐标:groupId, artifactId, version
    • 依赖声明:dependencies
    • 插件配置:plugins
    • 构建配置:build
<project> 
  <modelVersion>4.0.0</modelVersion> 
  <groupId>com.example</groupId> 
  <artifactId>my-app</artifactId> 
  <version>1.0.0</version> 
  <packaging>jar</packaging> 
  
  <dependencies> 
  	<dependency> 
  		<groupId>junit</groupId> 
  		<artifactId>junit</artifactId> 
  		<version>4.13.2</version> 
  		<scope>test</scope> 
  		</dependency>
	</dependencies> 
</project>
  • 依赖管理:Maven 通过中央仓库(Maven Central)管理依赖,支持依赖传递和自动下载。依赖机制如下:
    • 传递性依赖:A 依赖 B,B 依赖 C ⇒ A 自动依赖 C
    • 依赖调解:采用“最近优先”的策略解决版本冲突问题
    • 作用域(scope):如 compile(默认)、test、provided、runtime 等
    • 依赖冲突排查:可采用maven dependency:tree查看依赖树
  • 生命周期:Maven 有三套标准生命周期:
    • default:处理项目部署(compile → test → package → install → deploy)
    • clean:清理项目
    • site:生成项目文档
  • 插件系统:Maven 插件用于扩展构建功能,如编译、测试、打包、部署等

Maven 生命周期流程:

flowchart LR
    A[validate<br/>校验项目] --> B[compile<br/>编译主代码]
    B --> C[test<br/>运行测试]
    C --> D[package<br/>打包制品]
    D --> E[verify<br/>集成检查]
    E --> F[install<br/>安装到本地仓库]
    F --> G[deploy<br/>发布到远程仓库]

    P1[maven-compiler-plugin] -.绑定.-> B
    P2[maven-surefire-plugin] -.绑定.-> C
    P3[maven-jar-plugin] -.绑定.-> D
    P4[maven-deploy-plugin] -.绑定.-> G

Maven 工作原理

  • 使用依赖解析机制,自动下载所需要的依赖项
  • 通过项目结构来组织代码
  • Maven 的构建过程是声明式的,开发者只需要定义构建目标,Maven 会自动执行

Maven 常用命令

命令说明
mvn clean清理 target 目录
mvn compile编译源代码
mvn test运行单元测试
mvn package打包项目
mvn install打包并安装到本地仓库
mvn deploy部署到远程仓库
mvn dependency:tree查看依赖树
mvn dependency:analyze分析依赖
mvn help:effective-pom查看有效 POM

Maven 常用插件配置

<!-- 编译插件 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
        <source>17</source>
        <target>17</target>
    </configuration>
</plugin>
 
<!-- Spring Boot 打包插件 -->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
 
<!-- 代码质量检查插件 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>3.3.1</version>
</plugin>

Maven 的优劣

  • 优势:
    • 简化依赖管理
    • 提供标准化的项目结构
    • 支持多模块项目构建
    • 插件生态系统丰富
  • 劣势:
    • 配置文件较为冗长
    • 对于复杂的构建需求,灵活性欠佳

2. Gradle

Gradle -- 灵活强大的下一代构建工具

Gradle是什么?

Gradle 是一个开源的自动构建工具,其几乎可以构建任何类型的软件,而不仅限于Java项目

核心理念与项目结构

  • 核心理念
    • 基于任务的构建模型:一切皆任务(Task)
    • DSL支持:支持 Groovy DSL 和 Kotlin DSL(类型安全,IDE友好)
    • 增量编译 & 构建缓存:只重新构建变化的部分,大幅提升速度
    • 混合模型:既有约定,也允许高度自定义
  • 项目结构(兼容Maven)
my-app/ 
├── build.gradle(.kts) 
├── settings.gradle(.kts) 
├── src/ 
│ ├── main/ 
│ └── test/ 
└── build/ ← 构建输出目录

核心概念

  • 构建脚本:build.gradle 是 Gradle 的核心配置文件,支持 Groovy 和 Kotlin DSL,语法简洁且功能强大
// 构建脚本示例:Kotlin DSL 
// build.gradle.kts 
plugins { 
	java 
	application 
} 
 
// 项目的组、模块和版本信息
group = "com.example" 
version = "1.0.0" 
 
// 仓库
repositories { 
	mavenCentral() 
} 
 
// 依赖项(插件)
dependencies { 
	implementation("org.apache.commons:commons-lang3:3.12.0") testImplementation("junit:junit:4.13.2") 
}
 
application { 
	mainClass.set("com.example.Main") 
}
  • 依赖管理:Gradle 支持从多种仓库(如 Maven Central、JCenter、本地仓库、私有仓库等)获取依赖项,并具备强大的依赖解析和版本控制能力
    • 同样支持传递依赖、版本冲突解决
    • 支持动态版本(如 1.+)、强制版本(force = true)、依赖排除
    • 使用 configurations 精细控制依赖作用域(如 implementation vs api)。implementation 不会传递给上层消费者,减少依赖泄露,提升编译速度
  • 任务系统:Gradle 使用任务(Task)来定义构建步骤,任务之间可以相互依赖,支持高度定制化的构建流程
/* gradle 的执行分为两个阶段:
配置阶段(configuration):解析脚本,创建Task对象
执行阶段(Execution):仅执行被请求的Task及其依赖
*/
tasks.register("hello") { 
	doLast { println("Hello from Gradle!") 
	} 
} 
tasks.named("build") { 
	dependsOn("hello") 
}
  • 插件系统:Gradle 插件用于扩展构建功能,如 Java 插件、Spring Boot 插件、Javadoc 插件等,支持自定义插件开发

Gradle 任务依赖图:

flowchart TD
    A[请求任务<br/>./gradlew build] --> B[配置阶段<br/>解析 settings 和 build 脚本]
    B --> C[生成任务图<br/>Task Graph]
    C --> D[执行 compileKotlin / compileJava]
    C --> E[执行 processResources]
    D --> F[执行 test]
    E --> G[执行 jar / bootJar]
    F --> H[执行 check]
    G --> I[执行 assemble]
    H --> J[build 完成]
    I --> J

    K[增量构建] -.减少重复执行.-> D
    L[Build Cache] -.复用历史产物.-> G

Gradle 常用命令

命令说明
./gradlew clean清理构建目录
./gradlew build完整构建项目
./gradlew test运行测试
./gradlew bootRun运行 Spring Boot 应用
./gradlew dependencies查看依赖树
./gradlew tasks列出所有可用任务
./gradlew wrapper --gradle-version 8.5升级 Gradle Wrapper

Gradle 多模块项目配置

// settings.gradle.kts
rootProject.name = "my-multi-module-project"
include("module-a", "module-b", "module-c")
 
// build.gradle.kts (根项目)
plugins {
    java
    `java-library`
    id("org.springframework.boot") version "3.2.0" apply false
}
 
allprojects {
    group = "com.example"
    version = "1.0.0"
    
    repositories {
        mavenCentral()
    }
}
 
subprojects {
    apply(plugin = "java")
    
    dependencies {
        testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
    }
    
    tasks.test {
        useJUnitPlatform()
    }
}

Gradle 版本冲突解决

// 方式1:强制指定版本
configurations.all {
    resolutionStrategy {
        force("com.google.guava:guava:32.1.3-jre")
    }
}
 
// 方式2:排除特定依赖
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web") {
        exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
    }
}
 
// 方式3:使用 platform 统一管理版本
dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:3.2.0"))
    implementation("org.springframework.boot:spring-boot-starter-web")
}

3. 版本冲突解决实战

依赖冲突是项目构建中最常见的问题之一

3.1 冲突产生的原因

当项目依赖多个库,而这些库又依赖同一个库的不同版本时,就会产生冲突。

你的项目
├── A.jar (依赖 C.jar v1.0)
└── B.jar (依赖 C.jar v2.0)

3.2 Maven 冲突解决

查看依赖树

mvn dependency:tree -Dverbose

解决策略

<!-- 方式1:使用 exclusion 排除传递依赖 -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>module-a</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </exclusion>
    </exclusions>
</dependency>
 
<!-- 方式2:直接声明所需版本(就近原则) -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
</dependency>
 
<!-- 方式3:使用 dependencyManagement 统一管理 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>3.2.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3.3 Gradle 冲突解决

查看依赖报告

./gradlew dependencies --configuration runtimeClasspath

解决策略

// build.gradle.kts
 
// 方式1:强制指定版本
configurations.all {
    resolutionStrategy {
        force("com.google.guava:guava:32.1.3-jre")
        
        // 缓存动态版本的时间
        cacheDynamicVersionsFor(10, TimeUnit.MINUTES)
        cacheChangingModulesFor(4, TimeUnit.HOURS)
    }
}
 
// 方式2:排除特定依赖
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web") {
        exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
    }
}
 
// 方式3:使用 platform / BOM 管理版本
dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:3.2.0"))
    implementation(platform("org.springframework.cloud:spring-cloud-dependencies:2023.0.0"))
    
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
}
 
// 方式4:严格版本约束
dependencies {
    implementation("org.apache.commons:commons-lang3") {
        version {
            strictly("[3.8, 4.0[")
            prefer("3.12.0")
        }
    }
}

4. CI/CD 集成

4.1 GitHub Actions + Maven

# .github/workflows/maven.yml
name: Java CI with Maven
 
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
 
jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: maven
    
    - name: Build with Maven
      run: mvn -B package --file pom.xml
    
    - name: Run tests
      run: mvn test
    
    - name: Upload artifact
      uses: actions/upload-artifact@v4
      with:
        name: jar-artifact
        path: target/*.jar

4.2 GitHub Actions + Gradle

# .github/workflows/gradle.yml
name: Java CI with Gradle
 
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
 
jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'
    
    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@v3
      with:
        gradle-version: wrapper
    
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    
    - name: Build with Gradle
      run: ./gradlew build
    
    - name: Run tests
      run: ./gradlew test
    
    - name: Generate test report
      uses: dorny/test-reporter@v1
      if: success() || failure()
      with:
        name: Gradle Tests
        path: build/test-results/test/*.xml
        reporter: java-junit

4.3 Docker 集成

Maven + Docker

<!-- pom.xml 中添加 Docker 插件 -->
<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>dockerfile-maven-plugin</artifactId>
    <version>1.4.13</version>
    <configuration>
        <repository>${project.artifactId}</repository>
        <tag>${project.version}</tag>
        <buildArgs>
            <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
        </buildArgs>
    </configuration>
</plugin>
# Dockerfile
FROM eclipse-temurin:17-jre-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

Gradle + Docker

// build.gradle.kts
plugins {
    id("com.google.cloud.tools.jib") version "3.4.0"
}
 
jib {
    from {
        image = "eclipse-temurin:17-jre-alpine"
    }
    to {
        image = "your-registry/${project.name}"
        tags = setOf("latest", project.version.toString())
    }
    container {
        mainClass = "com.example.Application"
        ports = listOf("8080")
    }
}
# 构建并推送镜像
./gradlew jib
 
# 仅构建本地镜像
./gradlew jibDockerBuild

5. 最佳实践

5.1 选择建议

场景推荐工具理由
传统 Java 企业项目Maven生态成熟,团队熟悉度高
Android 开发GradleGoogle 官方支持,构建速度快
Kotlin 项目GradleKotlin DSL 原生支持
多模块复杂项目Gradle配置灵活,构建性能高
快速原型开发Gradle配置简洁,开发体验好
遗留系统维护Maven稳定可靠,变更风险低

5.2 通用最佳实践

  1. 版本管理

    • 使用语义化版本 (Semantic Versioning)
    • 快照版本仅用于开发环境
    • 发布版本必须稳定可复现
  2. 依赖管理

    • 定期更新依赖版本
    • 使用工具检查安全漏洞 (OWASP Dependency Check)
    • 锁定版本,避免动态版本带来的不确定性
  3. 构建优化

    • 使用构建缓存
    • 并行构建
    • 增量构建
  4. 文档化

    • README 中包含构建说明
    • 记录环境要求 (JDK 版本等)
    • 提供本地开发快速开始指南

6. 总结

结语

Maven 和 Gradle 并非对立,而是代表了构建工具演进的不同阶段。Maven 奠定了标准化基础,而 Gradle 在其之上提供了更高的灵活性与性能。理解它们的底层原理,能帮助我们在复杂项目中做出更合理的技术选型。

选择建议:

  • 新项目优先考虑 Gradle,特别是 Kotlin 项目
  • 维护老项目继续使用 Maven
  • 团队技能也是重要考量因素
特性MavenGradle
配置语言XMLGroovy/Kotlin
依赖管理中央仓库支持多种仓库
构建生命周期标准化可自定义
插件系统丰富但较为固定更灵活,可自定义插件
构建性能一般更快,支持增量构建
项目结构标准化更灵活
学习曲线较平缓较陡峭
IDE 支持优秀优秀
社区活跃度高高

学习资源