Maven 全面学习笔记:从基础到实战

1. Maven 简介

1.1 Maven 是什么

  • Maven 的本质是一个项目管理工具,将项目开发和管理过程抽象成项目对象模型(POM,Project Object Model)

  • POM 通过pom.xml文件描述项目的结构、依赖、构建规则等核心信息,是 Maven 管理项目的基础。

    Maven POM模型示意图

1.2 Maven 的作用

  • 项目构建:提供标准的跨平台自动化构建方式(编译、测试、打包、部署等),无需手动维护构建脚本。
  • 依赖管理:自动管理项目依赖的资源(如 JAR 包),解决依赖下载、版本冲突等问题。
  • 统一开发结构:规定标准的项目目录结构(如src/main/java存放主代码、src/test/java存放测试代码),减少团队协作成本。

2. Maven 基础概念

2.1 Maven 仓库

Maven 仓库是存储项目依赖(JAR 包、插件等)的地方,分为三类:

  • 本地仓库:位于开发者本地的仓库(默认路径:~/.m2/repository),用于缓存下载的依赖,避免重复下载。

  • 远程仓库:位于网络中的仓库(如 Maven 中央仓库、阿里云仓库、公司私有仓库),提供依赖的下载源。

  • 中央仓库:Maven 官方维护的远程仓库(默认地址:https://repo.maven.apache.org/maven2),包含绝大多数开源依赖。

    image

    注意:Maven 查找依赖的顺序为:本地仓库 → 远程仓库(按配置顺序)→ 中央仓库。

2.2 远程仓库配置

远程仓库配置用于指定依赖的下载来源,可在项目级(pom.xml)或全局级(settings.xml)配置,优先级:项目级 > 用户级settings.xml~/.m2/)> 全局级settings.xml(Maven 安装目录conf/)。

<!-- pom.xml 或 settings.xml 中配置 -->
<repositories>
  <repository>
    <id>aliyun-public</id> <!-- 仓库唯一标识(必须唯一) -->
    <name>Aliyun Public Repository</name> <!-- 仓库名称(自定义) -->
    <url>https://maven.aliyun.com/repository/public</url> <!-- 仓库地址 -->
    
    <!-- 发布版本(RELEASE)配置 -->
    <releases>
      <enabled>true</enabled> <!-- 是否允许下载发布版本 -->
      <updatePolicy>daily</updatePolicy> <!-- 更新策略:always/daily/never -->
    </releases>
    
    <!-- 快照版本(SNAPSHOT)配置 -->
    <snapshots>
      <enabled>false</enabled> <!-- 生产环境建议关闭快照版本下载 -->
      <updatePolicy>always</updatePolicy>
    </snapshots>
  </repository>
</repositories>

注意releasessnapshotsenabled需根据环境调整:开发环境可开启快照版本获取最新依赖,生产环境建议关闭以保证稳定性。

2.3 镜像仓库配置

镜像仓库用于替代指定的远程仓库(通常用于加速下载,如国内镜像替代中央仓库),仅能在settings.xml中配置。

<settings>
  <mirrors>
    <mirror>
      <id>aliyun-central</id> <!-- 镜像唯一标识 -->
      <name>Aliyun Mirror for Central</name> <!-- 镜像名称 -->
      <url>https://maven.aliyun.com/repository/central</url> <!-- 镜像地址 -->
      <mirrorOf>central</mirrorOf> <!-- 替代的仓库ID(central为默认中央仓库) -->
    </mirror>
  </mirrors>
</settings>

关键说明

  • mirrorOf支持通配符:*表示替代所有仓库,repo1,repo2表示替代指定仓库,*,!private-repo表示替代所有仓库但排除私有仓库(!为排除符)。
  • 谨慎使用mirrorOf=*:可能覆盖公司私有仓库,导致私有依赖无法下载。

3. 依赖管理

3.1 依赖传递

  • 定义:当项目 A 依赖 B,B 依赖 C 时,A 会自动继承 B 对 C 的依赖(无需手动声明 C)。
  • 示例:A→B(依赖 C),则 A 间接依赖 C。
  • 优势:简化依赖声明;风险:可能引入同一依赖的不同版本,导致冲突。

3.2 可选依赖(optional)

  • 作用:控制依赖是否传递给间接引用的项目。
  • 配置:通过optional=true阻止依赖传递(默认false,允许传递)。
<!-- 项目B中配置:log4j为可选依赖 -->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
  <optional>true</optional> <!-- 依赖B的项目A不会自动继承log4j -->
</dependency>

场景:项目提供多种可选实现(如支持 MySQL 和 Oracle 两种数据库驱动),由引用方手动选择所需依赖。

3.3 依赖排除(exclusion)

  • 作用:主动移除传递依赖中的指定依赖(解决版本冲突或移除无用依赖)。
  • 配置:通过<exclusions>标签排除,无需指定版本(仅需 groupId 和 artifactId)。
<!-- 项目A依赖B,B传递依赖C:1.0,A需排除C:1.0并使用C:2.0 -->
<dependency>
  <groupId>com.example</groupId>
  <artifactId>B</artifactId>
  <version>1.0</version>
  <exclusions>
    <exclusion>
      <groupId>com.example</groupId>
      <artifactId>C</artifactId> <!-- 排除B传递的所有版本的C -->
    </exclusion>
  </exclusions>
</dependency>

<!-- 手动声明C:2.0 -->
<dependency>
  <groupId>com.example</groupId>
  <artifactId>C</artifactId>
  <version>2.0</version>
</dependency>

3.4 依赖范围(scope)

依赖范围控制依赖在项目生命周期(编译、测试、运行、打包)中的生效范围,常见范围如下:

范围 主代码编译 测试代码编译 测试运行 项目运行 打包包含 传递性 典型示例
compile 完全传递 Spring、MyBatis
test 不传递 JUnit、Mockito
provided ✅* 不传递 Servlet API、JSP API
runtime 部分传递 JDBC 驱动、JSON 解析库
system ✅* 不传递 本地特定路径的 JAR

3.4.1 各范围详解

  • compile(默认):全生命周期生效,打包时包含,适用于项目运行必需的依赖(如框架核心库)。

  • test:仅在测试阶段生效(编译和运行测试代码),不参与打包,适用于测试工具(如 JUnit)。

  • provided:编译和测试阶段生效,但运行时由环境提供(如 Tomcat 提供 Servlet API),打包时不包含,避免冲突。

  • runtime:编译阶段不生效(仅依赖接口),运行和测试阶段生效(需具体实现),适用于 “接口 - 实现” 分离的依赖(如 JDBC 驱动)。

  • system:与provided类似,但依赖需手动指定本地路径(通过systemPath),不推荐使用(移植性差)。

    <dependency>
      <groupId>com.example</groupId>
      <artifactId>local-jar</artifactId>
      <version>1.0</version>
      <scope>system</scope>
      <systemPath>${project.basedir}/lib/local-jar.jar</systemPath> <!-- 本地路径 -->
    </dependency>
    

3.5 依赖冲突解决

当同一依赖的多个版本被传递引入时,Maven 通过以下规则解决冲突:

  1. 路径最近原则:层级越近的依赖优先级越高。
    示例:A 直接依赖 C:1.0,同时 A→B→C:2.0,则 A 使用 C:1.0(直接依赖层级更近)。
  2. 声明顺序优先:路径长度相同时,pom.xml中先声明的依赖版本生效。
    示例:A 依赖 B 和 C,两者都传递依赖 C:1.0 和 C:2.0,若 B 在 C 之前声明,则使用 B 传递的版本。
  3. 手动排除:通过<exclusions>主动移除冲突版本(见 3.3 节)。
  4. 版本锁定(dependencyManagement):在父工程的dependencyManagement中声明版本,子工程引用时无需指定版本,统一管控。
<!-- 父工程 pom.xml -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>C</artifactId>
      <version>1.0</version> <!-- 锁定版本 -->
    </dependency>
  </dependencies>
</dependencyManagement>

<!-- 子工程 pom.xml -->
<dependency>
  <groupId>com.example</groupId>
  <artifactId>C</artifactId> <!-- 无需指定版本,继承父工程 -->
</dependency>

工具:通过mvn dependency:tree命令查看依赖树,快速定位冲突版本。

4. 生命周期与插件

4.1 三大生命周期

Maven 的生命周期是一系列有序的阶段(Phase),用于定义项目构建的完整流程,每个生命周期内的阶段按顺序执行(执行后续阶段会自动触发前置阶段)。

4.1.1 clean 生命周期(清理构建产物)

  • pre-clean:清理前的准备工作
  • clean:删除target目录(核心阶段)
  • post-clean:清理后的收尾工作

4.1.2 default 生命周期(核心构建流程)

  • validate:校验项目配置是否完整
  • compile:编译主代码(src/main/javatarget/classes
  • test-compile:编译测试代码(src/test/javatarget/test-classes
  • test:运行测试代码(如 JUnit 测试)
  • package:将编译后的代码打包(如 JAR、WAR)
  • install:将包安装到本地仓库(供本地其他项目依赖)
  • deploy:将包部署到远程仓库(供团队共享)

4.1.3 site 生命周期(生成项目文档)

  • pre-site:生成文档前的准备
  • site:生成 HTML 格式的项目文档(如 API 文档、说明文档)
  • post-site:生成文档后的处理
  • site-deploy:将文档部署到服务器

4.2 Maven 插件

Maven 核心仅定义生命周期,具体功能由插件(Plugin) 实现。每个插件包含多个目标(Goal),对应具体操作(如编译、测试)。

4.2.1 插件与生命周期的绑定

插件目标可绑定到生命周期的阶段,当执行阶段时,自动触发对应的目标。例如:

  • maven-compiler-plugin:compile 绑定到 compile 阶段(负责编译主代码)
  • maven-surefire-plugin:test 绑定到 test 阶段(负责运行测试)
  • maven-jar-plugin:jar 绑定到 package 阶段(负责生成 JAR 包)

4.2.2 常用插件及配置

  • 编译插件(maven-compiler-plugin):指定 Java 版本

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.1</version>
          <configuration>
            <source>1.8</source> <!-- 源代码版本 -->
            <target>1.8</target> <!-- 编译后字节码版本 -->
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  • 测试插件(maven-surefire-plugin):配置测试用例执行规则(如跳过测试)

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.0.0-M5</version>
      <configuration>
        <skipTests>true</skipTests> <!-- 跳过测试(打包时常用) -->
      </configuration>
    </plugin>
    

注意:插件需指定版本,避免使用默认版本(可能过时);可通过mvn 插件前缀:目标直接执行插件目标(如mvn compiler:compile)。

5. 聚合与继承

聚合与继承是多模块项目的核心管理方式,两者常结合使用但目标不同。

5.1 聚合(Aggregation)

  • 作用:将多个子模块聚合到父工程,实现 “一键构建”(如mvn package可同时构建所有子模块)。

  • 配置:父工程pom.xml中通过<modules>声明子模块路径,父工程打包类型必须为pom

    <!-- 父工程 pom.xml -->
    <project>
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.example</groupId>
      <artifactId>parent</artifactId>
      <version>1.0.0</version>
      <packaging>pom</packaging> <!-- 聚合工程必须为pom类型 -->
      
      <modules>
        <module>module-common</module> <!-- 子模块1(相对路径) -->
        <module>module-service</module> <!-- 子模块2 -->
      </modules>
    </project>
    

注意:父工程通常不包含业务代码,仅作为构建入口;子模块路径为相对路径(如../module-common)。

5.2 继承(Inheritance)

  • 作用:子工程继承父工程的配置(依赖、插件、属性等),减少重复配置。

  • 配置

    1. 父工程在dependencyManagement中声明可继承的依赖(仅声明,不实际引入);
    2. 子工程通过<parent>标签指定父工程,引用依赖时无需指定版本。
    <!-- 父工程 pom.xml -->
    <project>
      <packaging>pom</packaging>
      <dependencyManagement>
        <dependencies>
          <dependency> <!-- 声明可继承的依赖 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.7.0</version>
          </dependency>
        </dependencies>
      </dependencyManagement>
    </project>
    
    <!-- 子工程 pom.xml -->
    <project>
      <parent>
        <groupId>com.example</groupId>
        <artifactId>parent</artifactId>
        <version>1.0.0</version>
        <relativePath>../pom.xml</relativePath> <!-- 父工程pom路径 -->
      </parent>
      
      <artifactId>module-service</artifactId>
      <dependencies>
        <dependency> <!-- 引用时无需版本,继承父工程 -->
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
        </dependency>
      </dependencies>
    </project>
    

聚合与继承的区别

  • 聚合:解决 “多模块批量构建” 问题(父管构建);
  • 继承:解决 “配置复用” 问题(子用父配置)。

6. 属性

Maven 属性用于定义可复用的常量(如版本号、路径),支持动态引用,简化配置维护。

6.1 属性类型

  1. 内置属性:Maven 预定义,可直接使用。
    • ${project.groupId}/${groupId}:项目 groupId
    • ${project.artifactId}/${artifactId}:项目 artifactId
    • ${project.version}/${version}:项目版本
    • ${project.basedir}:项目根目录路径
  2. 自定义属性:在pom.xml<properties>中定义,格式为${属性名}
  3. Settings 属性:引用settings.xml中的配置,如${settings.localRepository}(本地仓库路径)。
  4. 环境变量属性:引用系统环境变量,如${env.JAVA_HOME}(Java 安装路径)。

6.2 自定义属性示例

<project>
  <properties>
    <spring.boot.version>2.7.0</spring.boot.version> <!-- 框架版本 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 编码格式 -->
    <java.version>1.8</java.version> <!-- Java版本 -->
  </properties>
  
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>${spring.boot.version}</version> <!-- 引用自定义属性 -->
    </dependency>
  </dependencies>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>${java.version}</source> <!-- 引用Java版本 -->
          <target>${java.version}</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

优势:版本升级时只需修改<properties>中的值,无需逐个修改依赖,减少出错风险。

7. 版本管理

7.1 工程版本类型

Maven 通过版本号区分项目的开发阶段,核心分为两类:

  1. SNAPSHOT(快照版本)
    • 格式:x.y.z-SNAPSHOT(如1.0.0-SNAPSHOT
    • 特性:开发中的临时版本,每次构建会自动从远程仓库拉取最新快照(即使本地已存在)。
    • 场景:团队内部开发时,模块间的临时依赖(确保使用最新开发成果)。
  2. RELEASE(发布版本)
    • 格式:x.y.z(如1.0.0
    • 特性:正式发布的稳定版本,版本固定,Maven 不会主动更新(本地存在则直接使用)。
    • 场景:对外发布的正式版本(如生产环境部署)。

7.2 版本号约定

版本号遵循 “主版本。次版本。增量版本 - 里程碑版本” 规则,格式:主版本.次版本.增量版本-里程碑版本

  • 主版本:重大功能迭代,不兼容旧版本(如2.0.0相比1.0.0有架构级变更)。
  • 次版本:新增功能,兼容主版本(如1.1.0相比1.0.0新增功能但兼容)。
  • 增量版本:仅修复 bug,无新功能(如1.0.1相比1.0.0修复漏洞)。
  • 里程碑版本:标注开发阶段(如1.0.0-BETA1.0.0-RC1(候选发布版))。

示例

  • 3.2.1:主版本 3,次版本 2,增量版本 1(正式发布)。
  • 2.0.0-SNAPSHOT:主版本 2 的开发中快照。
  • 1.3.0-RC2:次版本 3 的第二个候选发布版。

8. 资源配置

Maven 默认将src/main/resources(主资源)和src/test/resources(测试资源)下的文件视为资源,构建时复制到target/classestarget/test-classes。实际开发中常需自定义资源管理。

8.1 资源配置多文件维护

当资源文件较多时,可按功能拆分到不同目录(如src/main/config/dbsrc/main/config/log),通过<resources>标签指定加载路径。

<build>
  <resources>
    <!-- 加载默认资源目录 -->
    <resource>
      <directory>src/main/resources</directory>
    </resource>
    <!-- 加载自定义资源目录 -->
    <resource>
      <directory>src/main/config</directory> <!-- 自定义目录 -->
      <includes> <!-- 仅包含指定文件 -->
        <include>*.properties</include>
        <include>*.xml</include>
      </includes>
      <excludes> <!-- 排除不需要的文件 -->
        <exclude>dev-*.properties</exclude>
      </excludes>
    </resource>
  </resources>
</build>

8.2 配置文件引用 POM 属性

资源文件(如.properties.xml)可引用 POM 中定义的属性(需开启资源过滤),实现配置动态化。

步骤

  1. 在 POM 中定义属性;
  2. 在资源文件中通过${属性名}引用;
  3. 开启资源过滤(filtering=true)。
<!-- 1. POM中定义属性 -->
<properties>
  <app.name>user-service</app.name>
  <app.version>1.0.0</app.version>
</properties>

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering> <!-- 2. 开启过滤,解析属性 -->
    </resource>
  </resources>
</build>

<!-- 3. 资源文件 src/main/resources/application.properties -->
app.name=${app.name}
app.version=${app.version}
server.port=8080

构建后效果target/classes/application.properties中自动替换为:

app.name=user-service
app.version=1.0.0
server.port=8080

9. 多环境开发配置

同一项目在开发(dev)、测试(test)、生产(prod)环境的配置(如数据库地址、端口)不同,需通过多环境配置实现隔离。

9.1 多环境配置(profiles)

通过pom.xml<profiles>标签定义不同环境的属性,每个环境对应唯一id

<profiles>
  <!-- 开发环境 -->
  <profile>
    <id>dev</id>
    <properties>
      <db.url>jdbc:mysql://localhost:3306/dev_db</db.url>
      <server.port>8081</server.port>
    </properties>
    <activation>
      <activeByDefault>true</activeByDefault> <!-- 默认激活开发环境 -->
    </activation>
  </profile>
  
  <!-- 测试环境 -->
  <profile>
    <id>test</id>
    <properties>
      <db.url>jdbc:mysql://test-server:3306/test_db</db.url>
      <server.port>8082</server.port>
    </properties>
  </profile>
  
  <!-- 生产环境 -->
  <profile>
    <id>prod</id>
    <properties>
      <db.url>jdbc:mysql://prod-server:3306/prod_db</db.url>
      <server.port>80</server.port>
    </properties>
  </profile>
</profiles>

9.2 加载指定环境

方式 1:资源文件关联环境

在资源文件中引用profiles的属性,结合资源过滤动态生成配置:

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering> <!-- 允许资源文件引用profile属性 -->
    </resource>
  </resources>
</build>

资源文件application.properties中引用:

spring.datasource.url=${db.url}
server.port=${server.port}

方式 2:命令行激活环境

通过-P参数指定环境id,覆盖默认激活的环境:

# 打包测试环境(使用test profile的配置)
mvn package -Ptest

# 打包生产环境
mvn package -Pprod

优势:实现 “一次打包,多环境部署”,避免因环境配置不同重复打包。

10. 应用实践:

10.1 Spring Boot 与 Cloud 的三个核心依赖管理组件

  1. Spring Boot Starter Parent

    • 作用:Spring Boot 项目的基础父工程,定义了默认 Java 版本、编码格式、依赖版本等,子工程继承后可简化配置。

    • 配置:

      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
      </parent>
      
  2. Spring Cloud Dependencies

    • 作用:Spring Cloud 官方组件的版本管理 BOM(Bill of Materials),统一微服务组件版本(如 Eureka、Feign),避免版本冲突。

    • 配置:

      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.3</version>
            <type>pom</type>
            <scope>import</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
      
  3. Spring Cloud Alibaba Dependencies

    • 作用:管理阿里系微服务组件(如 Nacos、Sentinel)的版本,用法与 Spring Cloud Dependencies 类似。

参考资料

posted @ 2025-08-03 21:16  进击的小蔡鸟  阅读(150)  评论(0)    收藏  举报