Skip to content
第 45 / 250 章后端⏱ 10 分钟阅读

第 45 章:Maven 实战

学习目标

  • 掌握 Maven 坐标与依赖管理
  • 理解依赖传递与冲突解决
  • 掌握多模块项目结构
  • 学会配置国内镜像加速

一、Maven 是什么?

三大核心能力

能力解决的问题
依赖管理不用手动下 jar 包,声明坐标自动下载(含依赖的依赖)
项目构建标准化的编译、测试、打包、部署流程
项目结构规范所有 Java 项目长得一样,换项目零学习成本

二、标准目录结构

my-project/
├── pom.xml                          # 项目描述文件
├── src/
│   ├── main/
│   │   ├── java/                    # 主代码
│   │   └── resources/               # 配置文件(打包进 jar 根目录)
│   └── test/
│       ├── java/                    # 测试代码(不打包)
│       └── resources/               # 测试配置
└── target/                          # 构建产物(git 忽略)

约定优于配置:Maven 默认就知道去这些位置找文件,不用配置。

三、坐标:GAV

xml
<groupId>com.taskflow</groupId>       <!-- ① 组织标识,通常是倒写域名 -->
<artifactId>taskflow-api</artifactId> <!-- ② 项目名 -->
<version>1.0.0-SNAPSHOT</version>     <!-- ③ 版本号 -->
<packaging>jar</packaging>            <!-- ④ jar / war / pom -->

版本号规范(语义化版本)

主版本号.次版本号.修订号[-限定符]
   1   .   2   .   3   -SNAPSHOT

主版本号:不兼容的 API 变更
次版本号:向下兼容的功能新增
修订号  :向下兼容的 bug 修复
SNAPSHOT:开发中版本,每次构建都会重新拉取最新的
RELEASE :正式版,不可变

四、依赖范围 scope

xml
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
</dependency>
scope编译期测试期运行期打包典型场景
compile(默认)绝大多数依赖
providedLombok、Servlet API(容器提供)
runtimeJDBC 驱动(编译时用不到)
testJUnit、Mockito
import----只用于 dependencyManagement 导入 BOM

五、依赖传递与冲突

仲裁规则

  1. 路径最短优先A→B→C(1.0)A→D→E→C(2.0),选 1.0
  2. 路径相同则先声明优先:pom 中写在前面的赢

⚠️ 注意规则 1 的坑:版本高低不影响选择,很可能选到旧版本导致 NoSuchMethodError

排查与解决

bash
# ① 看依赖树,找出冲突来源
mvn dependency:tree

# ② 只看某个依赖的引入路径
mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind

# ③ 分析未使用/缺失的依赖
mvn dependency:analyze
xml
<!-- 方案 1:排除传递依赖 -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>some-lib</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 方案 2:直接声明(路径最短,一定生效)—— 推荐 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.3</version>
</dependency>

六、依赖管理与 BOM

xml
<!-- 父 pom:只声明版本,不引入依赖 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.5</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- 子模块:不写版本号,继承父 pom 的版本 -->
<dependencies>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
</dependencies>

好处:升级版本只改父 pom 一处,所有子模块统一。避免同一个库在不同模块用不同版本。

Spring Boot 的 BOM

xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
</parent>
<!-- 引入 Spring 相关依赖时都不用写版本号,Spring Boot 已经测试过兼容性 -->

七、生命周期与常用命令

重要:执行后面的阶段会自动执行前面所有阶段。mvn package 会先 compile 和 test。

bash
mvn clean                       # 删除 target
mvn compile                     # 编译
mvn test                        # 跑测试
mvn package                     # 打 jar/war
mvn install                     # 安装到本地仓库 ~/.m2
mvn deploy                      # 发布到远程仓库

# 常用组合
mvn clean package -DskipTests   # 打包跳过测试(-DskipTests 编译测试代码但不跑)
mvn clean install -Dmaven.test.skip=true   # 连测试代码都不编译

# 指定环境打包
mvn clean package -Pprod

# 并行构建(多模块项目提速明显)
mvn clean package -T 4

八、多模块项目

taskflow/
├── pom.xml                     # 父 pom,packaging = pom
├── taskflow-common/            # 公共工具、常量、异常
├── taskflow-domain/            # 实体、DTO、VO
├── taskflow-dao/               # Mapper、数据访问
├── taskflow-service/           # 业务逻辑
└── taskflow-web/               # Controller、启动类

父 pom

xml
<groupId>com.taskflow</groupId>
<artifactId>taskflow</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>          <!-- ① 父模块必须是 pom -->

<modules>                            <!-- ② 声明子模块 -->
    <module>taskflow-common</module>
    <module>taskflow-domain</module>
    <module>taskflow-dao</module>
    <module>taskflow-service</module>
    <module>taskflow-web</module>
</modules>

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <mybatis-plus.version>3.5.5</mybatis-plus.version>
</properties>

子模块

xml
<parent>
    <groupId>com.taskflow</groupId>
    <artifactId>taskflow</artifactId>
    <version>1.0.0</version>
</parent>

<artifactId>taskflow-service</artifactId>   <!-- groupId 和 version 继承父 pom -->

<dependencies>
    <dependency>
        <groupId>com.taskflow</groupId>
        <artifactId>taskflow-dao</artifactId>
        <version>${project.version}</version>   <!-- ③ 引用兄弟模块 -->
    </dependency>
</dependencies>

依赖只能单向。如果出现 service 依赖 web,说明分层错了。

九、多环境 profile

xml
<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>
            <filtering>true</filtering>                 <!-- ② 开启变量替换 -->
        </resource>
    </resources>
</build>

application.yml 中就能用 @env@(Spring Boot 把 Maven 占位符改成了 @,避免和 ${} 冲突):

yaml
spring:
  profiles:
    active: @env@

十、配置国内镜像(必做)

编辑 ~/.m2/settings.xml(Windows:C:\Users\你的用户名\.m2\settings.xml):

xml
<settings>
    <!-- ① 本地仓库位置(可选,默认 ~/.m2/repository) -->
    <localRepository>D:/maven-repo</localRepository>

    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>阿里云公共仓库</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <mirrorOf>central</mirrorOf>     <!-- ② 只镜像中央仓库 -->
        </mirror>
    </mirrors>

    <profiles>
        <profile>
            <id>jdk17</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>17</jdk>
            </activation>
            <properties>
                <maven.compiler.source>17</maven.compiler.source>
                <maven.compiler.target>17</maven.compiler.target>
            </properties>
        </profile>
    </profiles>
</settings>

⚠️ <mirrorOf> 不要写 *!写 * 会把所有仓库(包括公司私服)都指向阿里云,导致私服上的包下不下来。

十一、常见问题

bash
# 问题 1:依赖下不下来 / 下了一半损坏
# 清理 .lastUpdated 文件后重试
find ~/.m2 -name "*.lastUpdated" -delete
mvn clean install -U       # -U 强制更新 SNAPSHOT

# 问题 2:中文乱码
# 在 pom 的 properties 里加:
# <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

# 问题 3:IDEA 改了 pom 不生效
# 右键项目 → Maven → Reload Project

# 问题 4:想看最终生效的完整 pom(含继承)
mvn help:effective-pom

十二、本章小结

要点关键
坐标 GAVgroupId + artifactId + version
scopeLombok 用 provided,测试用 test
依赖冲突路径最短优先,用 dependency:tree 排查
dependencyManagement统一管版本,子模块不写版本号
多模块依赖单向,父 pom 用 packaging=pom
镜像阿里云,mirrorOf 不要写 *
提速-T 4 并行构建

动手练习

练习 1:基础题

创建一个三模块 Maven 项目(common / service / web),在 web 中调用 service 的方法,用 mvn clean package 打包成功。

练习 2:进阶题

mvn dependency:tree 分析一个 Spring Boot 项目,找出被引入了两个不同版本的依赖,并用 <exclusion> 解决冲突。


下一章第 46 章:项目结构规范

本站基于 VitePress 构建 · 由 Codebook 团队维护