从零到一:Maven 快速入门教程

发布于:2025-06-06 ⋅ 阅读:(23) ⋅ 点赞:(0)

Maven 简介

Maven 是什么

Maven 是 Apache 软件基金会开发的一个项目管理构建自动化工具,主要用于 Java 项目。它可以:

  • 管理项目的依赖(比如第三方库)
  • 自动构建项目(编译、打包)
  • 管理项目的生命周期(从创建到发布)
  • 管理插件(自动化任务,如测试、代码检查)

一个 Maven 工程遵循约定的目录结构,这种标准化的目录布局是 Maven 实现自动化构建过程中不可或缺的关键环节。Maven 推荐采用统一的目录结构,以确保项目的一致性和构建过程的高效性。

project-name/
├── src/
│   ├── main/
│   │   └── java/          # 主代码
│   └── test/
│       └── java/          # 测试代码
├── pom.xml                # Maven 配置文件

为什么使用 Maven?

传统 Java 项目可能要手动:

  • 下载 JAR 包(第三方库)
  • 配置类路径(classpath)
  • 编译源代码
  • 打包成 jar/war 文件

Maven 全部帮你做了!它像一个「项目自动管家」,你只需写好配置文件(pom.xml),Maven 会自动根据配置从仓库中拉取依赖。


安装 Maven

下载 Maven

下载地址:https://maven.apache.org/download.cgi

你可以下载最新版本的 Maven,也可以选择其他版本

然后里面选择自己对应的版本下载即可:

配置 Maven

解压文件

下载之后,解压到非中文的文件目录,如下:

配置本地仓库保存路径

Maven 会在每次从远程仓库获取第三方依赖后,将其缓存至本地指定的存储路径中。这一机制有效避免了在每次构建新项目时重复从远程仓库拉取相同依赖的情况,从而显著提升了开发效率并减少了不必要的网络开销。

<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
-->
<localRepository>D:\Environment\Repository</localRepository>

本地仓库默认值:用户/.m2/repository。由于本地仓库的默认位置是在用户的家目录下,而家目录往往是在 C 盘,也就是系统盘。将来 Maven 仓库中 jar 包越来越多,仓库体积越来越大,可能会拖慢 C 盘运行速度,影响系统性能。

配置国内仓库地址

Maven 下载 jar 包默认访问境外的中央仓库,而国外网站速度很慢。改成阿里云提供的镜像仓库,访问国内网站,可以让 Maven 下载 jar 包的时候速度更快。配置的方式是:

<mirror>
  <id>tencent</id>
  <name>tencent maven</name>
  <url>http://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url>
  <mirrorOf>central</mirrorOf>
</mirror>

<mirror>
  <id>nexus-aliyun</id>
  <mirrorOf>central</mirrorOf>
  <name>Nexus aliyun</name>
  <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

Maven 的核心概念

了解 pom.xml 文件

pom.xml(Project Object Model)是 Maven 项目的核心配置文件,其主要功能在于对项目的全面描述与定义,包括但不限于以下内容:

  • 项目元信息:定义项目的基本信息,如名称、版本、组织等标识性数据。
  • 依赖管理:声明项目所依赖的外部库及其版本,确保构建过程中的依赖解析与一致性。
  • 构建配置:指定项目的构建规则,包括编译、测试、打包等生命周期阶段的具体行为。
  • 插件与扩展:配置用于增强构建能力的插件及扩展模块,支持定制化构建需求。

通过 pom.xml,Maven 实现了项目结构的标准化与构建过程的自动化,为开发者提供了高效且可维护的项目管理工具。

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
  http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <!-- 当前Maven工程的坐标 -->
  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>demo</name>
  <description>Demo project for Spring Boot</description>
  
  <!-- 当前Maven工程的打包方式,可选值有下面三种: -->
  <!-- jar:表示这个工程是一个Java工程  -->
  <!-- war:表示这个工程是一个Web工程 -->
  <!-- pom:表示这个工程是“管理其他工程”的工程 -->
  <packaging>jar</packaging>
  
  <properties>
    <!-- 工程构建过程中读取源码时使用的字符集 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <!-- 当前工程所依赖的jar包 -->
  <dependencies>
    <dependency>
      <!-- 在dependency标签内使用具体的坐标依赖我们需要的一个jar包 -->
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <!-- scope标签配置依赖的范围 -->
      <scope>test</scope>
    </dependency>
  </dependencies>

  <!-- 构建配置 -->
  <build>
    <plugins>
      <!-- 编译插件 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.10.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

坐标

每一个依赖(或项目)都有三个关键标识:

  • groupId:组织名,一般是域名反写,如 org.springframework
  • artifactId:模块名,如 spring-core
  • version:版本号,如 5.3.10

合起来可以唯一确定一个库,如下:

<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>compile</scope>

项目首次运行时,会从远程仓库拉取依赖,并保存到本地仓库

上面坐标对应的 jar 包在 Maven 本地仓库中的位置:

Maven本地仓库根目录\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar

依赖范围

在 Maven 中,依赖范围(Dependency Scope)决定了一个依赖在构建过程中的可用性和传播性。

Maven 的生命周期如下:编译测试运行打包

不同的范围影响了该依赖在不同生命周期阶段的行为,即<scope>标签。

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <!-- scope标签配置依赖的范围 -->
  <scope>test</scope>
</dependency>

Maven 提供了几种常用的依赖范围类型,每种类型有不同的含义和使用场景。

生命周期

编译:将 Java 源代码文件(.java 文件)转换为 字节码(.class 文件),这是 Java 虚拟机(JVM)能够执行的代码。

测试: 执行单元测试(Unit Test)和集成测试(Integration Test)等。

运行:项目最终的运行时环境中。

打包:将当前项目打包成 Jar 或者 War 包。

compile

描述compile 范围是 Maven 依赖的默认范围。如果你没有显式指定依赖的范围,它会自动使用 compile 范围。compile 范围表示该依赖在编译、测试、运行时都需要。

可用阶段

  • 编译时:可以使用
  • 测试时:可以使用
  • 运行时:可以使用
  • 打包时:包含在最终的构件中

常见场景:适用于大多数的应用程序库,通常是核心依赖。

示例

<dependency>
    <groupId>com.example</groupId>
    <artifactId>some-library</artifactId>
    <version>1.0</version>
</dependency>
provided

描述provided 范围表示该依赖在编译测试时需要,但在运行时由容器或运行时环境提供。典型的例子是 Web 应用程序中的 Servlet API,它在 Web 容器(如 Tomcat)中已经提供,因此不需要在最终的构件中包含该依赖。

可用阶段

  • 编译时:可以使用
  • 测试时:可以使用
  • 运行时:不包含
  • 打包时:不包含(因为容器已经提供了该依赖)

常见场景:Web 项目中的容器依赖(如 servlet-api)或者一些 Java EE API。

示例

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>
runtime

描述runtime 范围表示该依赖在运行时需要,但在编译时不需要。通常这种范围适用于某些在运行时才需要的库,比如数据库驱动程序或者日志框架。

可用阶段

  • 编译时:不需要
  • 测试时:可以使用
  • 运行时:需要
  • 打包时:包含

常见场景:用于运行时需要的库,通常是数据库连接池、日志框架等。

示例

<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.49</version>
  <scope>runtime</scope>
</dependency>
test

描述test 范围表示该依赖仅在测试时需要。它不会在项目的运行时或者生产环境中包含。通常用于单元测试框架(如 JUnit 或 TestNG)和一些测试工具。

可用阶段

  • 编译时:可以使用
  • 测试时:可以使用
  • 运行时:不需要
  • 打包时:不包含

常见场景:单元测试框架(JUnit、TestNG)、模拟库(Mockito)等。

示例

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>
system

描述system 范围表示该依赖位于项目外部(如本地文件系统中)。它不是从远程仓库下载的,而是通过文件路径直接引用。system 范围的依赖必须指定 systemPath 属性,该路径指向文件的实际位置。

可用阶段

  • 编译时:可以使用
  • 测试时:可以使用
  • 运行时:需要
  • 打包时:包含

示例

<dependency>
  <groupId>com.example</groupId>
  <artifactId>some-library</artifactId>
  <version>1.0</version>
  <scope>system</scope>
  <systemPath>${project.basedir}/lib/some-library.jar</systemPath>
</dependency>

注意:不推荐使用 system 范围,因为它不具备跨平台的特性,依赖文件路径是硬编码的,缺乏可移植性。

import

描述import 范围主要用于导入 BOM(Bill of Materials)类型的依赖,它不会像普通的依赖那样将 JAR 文件直接添加到构建中,而是通过继承管理的一组版本。常用于依赖管理(dependencyManagement)的场景,通常在多模块项目中,或者用于引入其他项目的版本管理。

可用阶段

  • 编译时:不可用
  • 测试时:不可用
  • 运行时:不可用
  • 打包时:不可用

常见场景:用于在父项目中管理子模块的依赖版本,或引用第三方库的版本管理。

示例

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.3.7.RELEASE</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

依赖传递

Maven 支持依赖传递,即当一个项目依赖于某个库时,若该库本身依赖其他库,这些依赖会自动成为当前项目的依赖,直到最终没有更多的传递依赖为止。

假设我们有以下的项目依赖关系:

  • 项目 A 依赖项目 B
  • 项目 B 依赖项目 C
  • 项目 AB 不需要直接依赖 C,就可以引入 mysql-connector-javalombok 依赖

通过执行依赖树:

mvn dependency:tree

输出结果

[INFO] com.example:project-A:jar:1.0
[INFO] +- com.example:library-B:jar:1.0:compile
[INFO] |  +- com.example:library-C:jar:1.0:compile
[INFO] |  |  +- mysql:mysql-connector-java:jar:8.0.23:compile
[INFO] |  |  \- org.projectlombok:lombok:jar:1.18.20:provided
[INFO] \- (omitted for brevity)

依赖排除

如果 A 项目现在不需要使用 mysql 的依赖或者单独使用其他版本的依赖,应该如何处理呢?

Maven 提供了依赖排除的工程,避免版本冲突或者不必要的依赖,可以在 pom 文件中进行配置。

<!-- A项目的坐标 -->
<groupId>com.example</groupId>
<artifactId>project-A</artifactId>
<version>1.0</version>

<dependencies>
  <!-- A 项目依赖 B 项目 -->
  <dependency>
    <groupId>com.example</groupId>
    <artifactId>library-B</artifactId>
    <version>1.0</version>
    <exclusions>
      <!-- 排除 C 项目传递的 MySQL 依赖 -->
      <exclusion>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
</dependencies>

通过执行依赖树:

mvn dependency:tree

输出结果

[INFO] com.example:project-A:jar:1.0
[INFO] +- com.example:library-B:jar:1.0:compile
[INFO] |  +- com.example:library-C:jar:1.0:compile
[INFO] |  \- org.projectlombok:lombok:jar:1.18.20:provided
[INFO] \- (omitted for brevity)

MySQL 依赖没有出现在依赖树中,因为我们通过 <exclusions> 明确排除了 MySQL 依赖。

依赖循环

如果 A 工程依赖 B 工程,B 工程依赖 C 工程,C 工程又反过来依赖 A 工程,那么在执行构建操作时会报下面的错误:

[ERROR] [ERROR] The projects in the reactor contain a cyclic reference:

继承

Maven 的 继承 是一种在多模块项目或父子项目之间共享配置的机制。通过继承,子项目可以继承父项目的依赖、插件、属性、版本信息等配置,减少冗余的配置,提高可维护性。父子项目的关系是通过 parent 元素在 pom.xml 中实现的。

1. Maven 继承的基本概念

父项目(Parent Project):一个包含通用配置和版本管理的项目,其他子项目可以继承这些配置。

子项目(Child Project):继承父项目的 pom.xml 中定义的配置,包括依赖、插件、构建配置等

2. 如何使用 Maven 继承

2.1 创建父项目 pom.xml

父项目 pom.xml 中可以定义通用的配置,例如依赖版本、插件配置、构建配置等。然后,子项目通过 <parent> 元素继承这些配置。

父项目的 pom.xml 示例:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>parent-project</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging> <!-- 父项目的打包类型通常是 pom -->

  <!-- 指定子项目 -->
   <modules>
        <module>child-project</module>
    </modules>

  <dependencyManagement>
    <dependencies>
      <!-- 在父项目中声明的依赖版本会被所有子项目继承 -->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.9</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <pluginManagement>
      <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>
    </pluginManagement>
  </build>

</project>

在这个父项目中:

  • dependencyManagement:管理所有子项目中共享的依赖版本。
  • pluginManagement:定义了子项目共享的插件和插件的配置。
2.2 创建子项目 pom.xml

子项目通过 <parent> 元素来继承父项目的配置。子项目的 pom.xml 中需要指明父项目的 groupIdartifactIdversion

子项目的 pom.xml 示例:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <!-- 父项目坐标 -->
  <parent>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>child-project</artifactId>

  <dependencies>
    <!-- 继承父项目的 spring-core 依赖,版本由父项目提供 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- 子项目继承父项目的插件配置 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

在这个子项目中:

  • 子项目 继承了父项目的依赖管理,因此不需要在子项目中显式地指定 spring-core 的版本。
  • 子项目 继承了父项目的插件配置,因此不需要重新定义 maven-compiler-plugin 插件的版本和配置。

build 标签组成

生命周期插件

Maven 使用插件来执行不同生命周期阶段的任务,如编译、测试、打包等。每个生命周期都有多个阶段,而插件负责在这些阶段执行实际的任务。Maven 默认有多个生命周期,比如 清理生命周期默认生命周期站点生命周期。通过 build 标签中的 <plugins> 元素,你可以为不同的生命周期阶段指定插件。

常见的生命周期插件示例
  • maven-compiler-plugin:用于编译 Java 代码
  • maven-surefire-plugin:用于运行单元测试
  • maven-jar-plugin:用于打包 JAR 文件
  • maven-war-plugin:用于打包 WAR 文件
<build>
  <plugins>
    <!-- 编译插件 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.8.1</version>
      <configuration>
        <source>1.8</source>  <!-- 指定编译的 JDK 版本 -->
        <target>1.8</target>  <!-- 指定目标 JDK 版本 -->
      </configuration>
    </plugin>

    <!-- 单元测试插件 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.22.2</version>
    </plugin>

    <!-- JAR 打包插件 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>3.1.0</version>
      <configuration>
        <finalName>my-app</finalName>
      </configuration>
    </plugin>
  </plugins>
</build>

在上面的示例中,指定了三个常用的插件:

  • maven-compiler-plugin:用于指定编译的 JDK 版本。
  • maven-surefire-plugin:用于运行单元测试。
  • maven-jar-plugin:用于创建 JAR 文件,并指定输出文件名。

SpringBoot 定制化打包

Spring Boot 提供了非常灵活的构建和打包方式,你可以通过 Maven 插件来定制 Spring Boot 项目的打包过程。Spring Boot 使用 spring-boot-maven-plugin 插件来创建可执行的 JAR 或 WAR 文件。

<build>
  <plugins>
    <!-- Spring Boot Maven 插件 -->
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>2.5.2</version> <!-- 使用的 Spring Boot 版本 -->
      <configuration>
        <finalName>my-springboot-app</finalName> <!-- 打包后的文件名 -->
      </configuration>
    </plugin>
  </plugins>
</build>

Reference

  1. 超级详细的 Maven 教程(基础+高级)
  2. https://chatgpt.com/