一、什么是Maven?

Maven 是一个流行的项目管理工具和构建工具,可以对 Java 项目进行构建依赖管理。简单来说,它就像一个“助手”,使得项目的开发和管理变得更加简单和高效。

其它知识点补充:

项目管理工具:指的是一种软件,帮助开发者组织、管理和跟踪项目的进展。使用 Maven,可以轻松创建、配置和维护 Java 项目。

构建:是指将源代码转换为可以运行的程序的过程。Maven 可以自动化这个过程,它会根据指定的配置文件(通常是 pom.xml)来编译代码、打包程序、运行测试等。

依赖管理:是指处理项目中使用的库或其他组件(称为依赖)的过程。很多 Java 项目会依赖于其他库来实现某些功能。Maven 可以自动下载这些依赖,并确保它们的版本兼容,这样开发者就不需要手动管理这些库的版本和下载。

二、Maven有什么用?我们为什么要学Maven?

Maven 有什么用

  • 项目的自动构建,包括代码的编译、测试、打包、安装、部署等操作。

  • 依赖管理,项目使用到哪些依赖,可以快速完成导入,不需要手动导入jar包。

我们为什么要学 Maven?

  • 简单一句话,你不学maven就别想学别的东西(例如springcloud)!

三、Maven项目结构

Maven项目结构.jpg

而下面的pom.xml则是Maven的核心配置,也是整个项目的所有依赖、插件、以及各种配置的集合,它也是使用XML格式编写的,一个标准的pom配置长这样:

<?xml version="1.0" encoding="UTF-8"?>
<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.test</groupId>
    <artifactId>BookManage</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

pom配置解释:

project::根节点。

modelVersion::定义了当前模型的版本,不用管它。

groupId、artifactId、version::这三个元素合在一起,用于唯一区别每个项目,别人如果需要将我们编写的代码作为依赖,那么就必须通过这三个元素来定位我们的项目,我们称为一个项目的基本坐标,所有的项目一般都有自己的Maven坐标,因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了,无需再下载Jar文件,而是Maven自动帮助我们下载依赖并导入。

  • groupId 一般用于指定组名称,命名规则一般和包名一致,比如我们这里使用的是org.example,一个组下面可以有很多个项目。

  • artifactId 一般用于指定项目在当前组中的唯一名称,也就是说在组中用于区分于其他项目的标记。

  • version 代表项目版本,随着我们项目的开发和改进,版本号也会不断更新,我们可以手动指定当前项目的版本号,其他人使用我们的项目作为依赖时,也可以根据版本号进行选择(这里的SNAPSHOT代表快照,一般表示这是一个处于开发中的项目,正式发布项目一般只带版本号)

properties:这里一般都是一些变量和选项的配置,我们这里指定了JDK的源代码和编译版本为17,同时下面的源代码编码格式为UTF-8,无需进行修改。

四、Maven依赖导入

<dependencies>
    //里面填写的就是所有的依赖
</dependencies>

举例说明(以插入Lombok依赖为例):

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.36</version>
</dependency>

五、Maven依赖作用域

除了三个基本的属性用于定位坐标外,依赖还可以添加以下属性:

  • type:依赖的类型,对于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar

  • scope:依赖的范围

  • optional:标记依赖是否可选

  • exclusions:用来排除传递性依赖(一个项目有可能依赖于其他项目,就像我们的项目,如果别人要用我们的项目作为依赖,那么就需要一起下载我们项目的依赖,如Lombok)

1. type

  • 作用:定义依赖的类型,对应项目的打包类型(packaging)。

  • 默认值jar

  • 使用场景:当依赖的类型不是 jar 时,例如 war(Web 应用)、pom(父项目定义)、zip 等,需要显式声明。

  • 示例

    <dependency>
        <groupId>org.example</groupId>
        <artifactId>example-artifact</artifactId>
        <version>1.0.0</version>
        <type>war</type>
    </dependency>

2. scope(重点)

  • 作用:定义依赖的作用范围。主要范围包括:

    • compile(默认值):编译、测试和运行时都需要。

    • provided:编译和测试时需要,但运行时由容器(如 Tomcat)提供。

    • runtime:运行和测试时需要,编译时不需要。

    • test:仅测试时需要,编译和运行时不需要。

    • system:类似于 provided,但需要本地提供依赖路径(几乎不用)。

    • import:用于引入 BOM(Bill of Materials)依赖管理。

示例

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

3. optional

  • 作用:标记依赖是否为可选依赖,避免被传递到依赖的消费者(下游项目)。

  • 默认值false

  • 使用场景:当依赖是可选功能模块,不想影响下游项目时。

示例

<dependency>
    <groupId>org.example</groupId>
    <artifactId>optional-lib</artifactId>
    <version>1.0.0</version>
    <optional>true</optional>
</dependency>

4. exclusions

  • 作用:用于排除传递性依赖,防止不必要的依赖被引入。

  • 使用场景:当依赖的项目中包含的某些传递性依赖与你的项目冲突时。

示例

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.5.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

六、Maven安装、可选和排除

1、Maven 安装

问题导入:如何在其他项目中引入我们自己编写的Maven项目作为依赖使用呢?

先创建一个用于测试的简单项目:

<?xml version="1.0" encoding="UTF-8"?>
<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.test</groupId>
    <artifactId>TestMaven</artifactId>
    <version>1.0-SNAPSHOT</version>

    ...

</project>
public class TestUtils {
    public static void test() {
        System.out.println("家人们谁懂啊,蒸虾头,怎么会有人想吃我家鸽鸽下的蛋");
    }
}

接着我们点击右上角的Maven选项,然后执行install或直接在命令行中输入mvn install来安装我们自己的项目到本地Maven仓库中。

接着我们就可以在需要使用此项目作为依赖的其他项目中使用它了,只需要填写和这边一样的坐标:

<dependency>
    <groupId>com.test</groupId>
    <artifactId>TestMaven</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

接着我们就可以在项目中直接使用了:

public static void main(String[] args) {
    TestUtils.test();
}

2、Maven 中的可选依赖(Optional)

作用

  • Maven 的可选依赖属性 optional 用于标记某个依赖是否是可选的

  • 默认行为:Maven 会传递所有依赖的依赖(传递性依赖),即如果项目 A 依赖项目 B,而项目 B 又依赖项目 C,则项目 A 会默认获取 B 和 C。

  • 使用 optional 可以避免这种传递性。

场景

  • 当某个依赖是额外的功能模块,而下游项目可能不需要它时。

  • 避免引入无用的依赖,减少构建时间和冲突风险。

示例

项目 A 依赖项目 B,项目 B 依赖项目 C:

<dependency>
    <groupId>org.example</groupId>
    <artifactId>project-b</artifactId>
    <version>1.0.0</version>
</dependency>

项目 B 中声明 C 为可选依赖:

<dependency>
    <groupId>org.example</groupId>
    <artifactId>project-c</artifactId>
    <version>1.0.0</version>
    <optional>true</optional>
</dependency>

结果:项目 A 不会自动获取 C,除非手动添加。

3、Maven 中的依赖排除(Exclusions)

作用

  • 排除传递性依赖中不需要的模块。

  • 为什么需要?

    • 防止依赖冲突(例如不同版本的库)。

    • 删除多余的依赖以优化项目构建。

示例

项目 A 依赖项目 B,而项目 B 又依赖项目 C,但项目 A 不需要 C。

<dependency>
    <groupId>org.example</groupId>
    <artifactId>project-b</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.example</groupId>
            <artifactId>project-c</artifactId>
        </exclusion>
    </exclusions>
</dependency>

传递性依赖的层级示例

  1. 项目 A -> 项目 B -> 项目 C。

  2. 如果不需要项目 C,可通过 exclusions 将其排除,最终 A 只包含 B。

常见问题解决

  • 依赖冲突:当传递性依赖中某个库的多个版本引入冲突时,排除旧版本并手动引入所需版本。

  • 避免不必要的依赖:减少构建体积,避免不需要的模块。

Maven的继承

Maven 的继承机制允许项目的 POM 文件继承另一个 POM 文件中的配置,从而避免重复定义相同的配置。

1. 继承的特点

  • 层级结构:Maven 的继承是树状结构,一个子项目只能继承一个父项目。

  • 统一管理:父 POM 通常定义通用的依赖、插件、构建配置等,子 POM 自动继承。

  • 减少重复:多个子项目可以共享父 POM 中的配置,提高复用性。

2. 父 POM 定义

父 POM 中定义通用配置,例如:

x

3. 子项目继承

子项目通过 <parent> 标签指定父 POM:

x
  • 子项目继承父 POM 的依赖管理和插件配置。

  • 可在子项目中覆盖或补充父 POM 的配置。

多模块机制用于将一个大项目拆分为多个模块,每个模块都是一个独立的 Maven 项目,但可以共享配置和依赖。

Maven的多模块

1. 多模块结构

多模块项目通常有一个父 POM 和多个子模块,目录结构如下:

project-root/
├── pom.xml          # 父 POM
├── module-a/        # 子模块 A
│   └── pom.xml
├── module-b/        # 子模块 B
│   └── pom.xml

2. 父 POM(根 POM)

父 POM 除了继承机制的配置外,还需要声明模块:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>multi-module-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging> <!-- 必须是 pom 类型 -->

    <modules>
        <module>module-a</module>
        <module>module-b</module>
    </modules>
</project>

3. 子模块的 POM

子模块 POM 需要指定父项目:

<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>multi-module-project</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>module-a</artifactId>
</project>

4. 构建和依赖管理

  • 构建:在根目录运行 mvn install,会依次构建所有模块。

依赖管理:模块之间可以互相依赖:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>module-a</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

5. 优势

  • 模块化:每个模块负责单一功能,便于开发和维护。

  • 共享配置:通过父 POM 统一管理版本和插件。

  • 并行构建:Maven 可以并行构建多个模块,加快构建速度。

Maven测试和打包

在 IDEA 中,Maven 项目有多个生命周期,每个生命周期对应一个插件执行任务。常见的命令如下:

  • clean:清理 target 文件夹,解决缓存问题。

  • validate:验证项目的可用性。

  • compile:编译项目为 .class 文件。

  • install:将项目安装到本地仓库,供其他项目作为依赖使用。

  • verify:依次执行 validatecompilepackage 等生命周期阶段。

每个命令执行完后,IDE 会显示 BUILD SUCCESS,如果出现错误则会显示详细信息。

1. 测试命令

通过 test 命令,Maven 会自动运行 test 目录下的所有测试案例。测试类的名称需要以 Test 结尾(例如 MainTest),测试方法必须标注 @Test 注解。示例如下:

public class MainTest {

    @Test
    public void test() {
        System.out.println("我测你码");
    }
}

2. 打包命令

使用 package 命令可以将项目打包成 JAR 文件,生成的 JAR 文件可以作为其他项目的依赖或可执行文件。执行 package 时,会先自动执行测试命令,确保项目没有问题。如果希望跳过测试,可以使用以下命令:

mvn package -Dmaven.test.skip=true

3. 打包带依赖的 JAR 文件

通常,打包后的 JAR 文件仅包含项目自己的类,而缺少所需的外部依赖。如果要将项目及其依赖一起打包,可以使用 maven-assembly-plugin 插件。插件配置如下:

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <mainClass>com.test.Main</mainClass>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

配置好后,重新执行 package 命令,最终会生成两个 JAR 文件:一个是普通的 JAR 文件,另一个是包含所有依赖的 JAR 文件。我们只需要执行以下命令即可运行:

java -jar your-project-jar-with-dependencies.jar