## Maven

![image-20240229132137502](https://pic.bitday.top/i/2025/03/19/u6rxj1-2.png)

![image-20240229133408054](https://pic.bitday.top/i/2025/03/19/u6tcqw-2.png)

Maven仓库分为：

- 本地仓库：自己计算机上的一个目录(用来存储jar包)
- 中央仓库：由Maven团队维护的全球唯一的。仓库地址：https://repo1.maven.org/maven2/
- 远程仓库(私服)：一般由公司团队搭建的私有仓库

POM文件导入依赖的时候，先看本地仓库有没有，没有就看私服，再没有就从中央仓库下载。



### Maven创建/导入项目

#### **创建Maven项目**

![image-20250307174233390](https://pic.bitday.top/i/2025/03/19/u6u4ni-2.png)

勾选 **Create from archetype**（可选），也可以选择 **maven-archetype-quickstart** 等模版。

点击 Next，填写 GAV 坐标 。

GroupId：标识组织或公司（通常使用域名反写，如 `com.example`）

ArtifactId：标识具体项目或模块（如 `my-app`、`spring-boot-starter-web`）。

Version：标识版本号（如 `1.0-SNAPSHOT`、`2.7.3`）



#### 导入Maven项目

**（一）单独的Maven项目**

打开 IDEA，在主界面选择 Open（或者在菜单栏选择 File -> Open）。

在文件选择对话框中，定位到已有项目的根目录（包含 `pom.xml` 的目录）。

选择该目录后，IDEA 会检测到 `pom.xml` 并询问是否导入为 Maven 项目，点击 **OK** 或 **Import** 即可。

IDEA 会自动解析 `pom.xml`，下载依赖并构建项目结构。



**（二）在现有Maven项目中导入独立的Maven项目**

在已经打开的 IDEA 窗口中，使用 **File -> New -> Module from Existing Sources...** 

选择待导入项目的根目录（其中包含 `pom.xml`），IDEA 会将其导入为同一个工程下的另一个模块（Module）。

它们 看起来在一个工程里了，但**仍然是两个独立的** Maven 模块。



**（三）两个模块属于同一个工程下**

可以用一个父pom进行统一管理！

1.新建一个上层目录，如下，MyProject1和MyProject2的内容拷贝过去。

```xml
ParentProject/
├── pom.xml         <-- 父模块（聚合模块）
├── MyProject1/     <-- 子模块1
│   └── pom.xml
└── MyProject2/     <-- 子模块2
    └── pom.xml
```

2.创建父级pom

父模块 `pom.xml` 示例：

```xml
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>ParentProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>  //必写

    <modules>
        <module>MyProject1</module>  //必写
        <module>MyProject2</module>
    </modules>
</project>
```

3.修改子模块 `pom.xml` ，加上：

```xml
<parent>
    <groupId>com.example</groupId>
    <artifactId>ParentProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath> <!-- 可省略 -->
</parent>
```

如果子模块中无需与父级不同的配置，**可以不写**，就自动继承父级配置；若写了同名配置，则表示你想要**覆盖或合并**父级配置。

4.File -> Open选择父级的pom，会自动导入其下面两个项目。

**但是，仅仅这样无法让模块之间产生联动！需要在此基础上进行（四）的操作！**



**（四）通过 Maven 依赖引用**

如果你的两个模块之间存在依赖关系（如第一个模块需要使用第二个模块的类）还必须在 MyProject1 的 POM 里**显式声明**对 MyProject2 的依赖。

 MyProject1的pom.xml:

```xml
<project>
  <!-- 继承父 POM -->
  <parent>
    <groupId>com.example</groupId>
    <artifactId>ParentProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <artifactId>MyProject1</artifactId>
  <packaging>jar</packaging>

  <dependencies>
    <!-- 显式依赖于 MyProject2 -->
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>MyProject2</artifactId>
      <!-- 不写 <version>，Maven 会自动用父 POM 的 version -->
    </dependency>
    <!-- 其他依赖… -->
  </dependencies>
</project>
```

**如何打包？**

- 在**父 POM 根目录**执行 `mvn clean package`／`mvn clean install`。
- 先构建 MyProject2（因为 MyProject1 依赖它）

- 父 POM 自身不产物，模块的 JAR 都在各自的 `target/` 下。



### Maven坐标

什么是坐标？

* Maven中的坐标是 == 资源的唯一标识 ==  通过该坐标可以唯一定位资源位置
* 使用坐标来定义项目或引入项目中需要的依赖

![image-20240302131843540](https://pic.bitday.top/i/2025/03/19/u6ps37-2.png)

### 依赖管理

可以到mvn的中央仓库（https://mvnrepository.com/）中搜索获取依赖的坐标信息

```xml
<dependencies>
    <!-- 第1个依赖 : logback -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.11</version>
    </dependency>
    <!-- 第2个依赖 : junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
```

更改之后可以在界面上看到一个maven刷新按钮，点击一下就开始联网下载依赖了，成功后可以看到

![image-20240302133241227](https://pic.bitday.top/i/2025/03/19/u6szry-2.png)

#### 排除依赖

A依赖B，B依赖C，如果A不想将C依赖进来，可以同时排除C，被排除的资源**无需指定版本**。

```xml
<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>maven-projectB</artifactId>
    <version>1.0-SNAPSHOT</version>
   
    <!--排除依赖, 主动断开依赖的资源-->
    <exclusions>
    	<exclusion>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </exclusion>
    </exclusions>
</dependency>
```

#### 依赖范围

| **scope**值     | **主程序** | **测试程序** | **打包（运行）** | **范例**    |
| --------------- | ---------- | ------------ | ---------------- | ----------- |
| compile（默认） | Y          | Y            | Y                | log4j       |
| test            | -          | Y            | -                | junit       |
| provided        | Y          | Y            | -                | servlet-api |
| runtime         | -          | Y            | Y                | jdbc驱动    |

注意！！！这里的scope如果是`test`，那么它的作用范围在`src/test/java`下，在`src/main/java`下无法导包！



### Maven 多模块工程

父 POM 用 `<dependencyManagement>` 锁版本，子模块按需在 `<dependencies>` 中声明自己用的依赖。对“真正所有模块都要”的依赖，可以放到父 POM 顶层 `<dependencies>`，让它们自动继承。

父 POM（pom.xml）:

```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.0</version>
  <packaging>pom</packaging>

  <!-- 声明所有子模块 -->
  <modules>
    <module>service-a</module>
    <module>service-b</module>
  </modules>

  <!-- 1. 统一锁定版本号（子模块引用时不用写 <version>） -->
  <dependencyManagement>
    <dependencies>
      <!-- Spring Boot Web Starter -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.7.3</version>
      </dependency>
      <!-- Lombok -->
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <!-- 2. 所有模块都需要的“公共依赖”放这里，子模块自动继承 -->
  <dependencies>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <!-- 不用写 <version>，会从上面 dependencyManagement 拿 -->
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

```



子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>

  <!-- 继承父 POM -->
  <parent>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0.0</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <artifactId>service-a</artifactId>
  <packaging>jar</packaging>

  <dependencies>
    <!-- 1. 从父 dependencyManagement 拿版本，不需要写 <version> -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- 2. lombok 已经在父 POM 顶层 dependencies 引入，这里如果要用，也可不再声明 -->
  </dependencies>
</project>

```



父pom的`<packaging>pom</packaging>`表示它只是一个 POM 模块，不会产出任何可执行的 JAR。

子pom的`<relativePath>../pom.xml</relativePath>`告诉 Maven 去哪个相对路径找父 POM 文件



注意：如果子模块A依赖于B模块，那么B模块中的依赖会传递给A，比如B中引入了`org.apache.httpcomponents`，那么A模块的类中可以直接import这个库。反过来不行！大坑！



### Maven生命周期

主要关注以下几个：

• clean：移除上一次构建生成的文件 （Target文件夹）

• compile：编译 `src/main/java` 中的 Java 源文件至 `target/classes`

• test：使用合适的单元测试框架运行测试(junit)

• package：将编译后的文件打包，如：jar、war等

• install：将打包后的产物（如 `jar`）安装到本地仓库

**后面的生命周期执行的时候会自动执行前面所有生命周期！**



#### compile:

```text
src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           └── App.java
│   └── resources/
│       ├── application.yml
│       └── static/
│           └── logo.png
└── test/
    ├── java/
    │   └── com/
    │       └── example/
    │           └── AppTest.java
    └── resources/
        └── test-data.json

映射到 target/ 后：

target/
├── classes/                    ← 主代码和资源的输出根目录
│   ├── com/
│   │   └── example/
│   │       └── App.class       ← 编译自 src/main/java/com/example/App.java
│   ├── application.yml         ← 复制自 src/main/resources/application.yml
│   └── static/
│       └── logo.png            ← 复制自 src/main/resources/static/logo.png
└── test-classes/               ← 测试代码和测试资源的输出根目录
    ├── com/
    │   └── example/
    │       └── AppTest.class   ← 编译自 src/test/java/com/example/AppTest.java
    └── test-data.json          ← 复制自 src/test/resources/test-data.json

```



#### test：

**扫描** `src/test/java` 下所有符合默认命名规则的测试类：

- `**/Test*.java`
- `**/*Test.java`
- `**/*TestCase.java`

**编译** 这些测试类到 `target/test-classes`。

**逐个执行**（默认是串行）所有这些编译后的测试类。



#### package：

打包失败的把test步骤去掉！

![image-20250514125309214](https://pic.bitday.top/i/2025/05/14/kq2fpz-0.png)



### Maven镜像加速

maven构建依赖可能比较慢，需创建 `.mvn/settings.xml`   （这个提速非常多！务必添加）

```xml
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0
                              https://maven.apache.org/xsd/settings-1.1.0.xsd">
  <mirrors>
    <mirror>
      <id>aliyun</id>
      <name>aliyun maven</name>
      <url>https://maven.aliyun.com/repository/public</url>
      <mirrorOf>central,apache.snapshots</mirrorOf>
    </mirror>
  </mirrors>
</settings>
```

使用阿里云镜像加速，`.mvn` 放在项目根目录下