390 lines
12 KiB
Markdown
390 lines
12 KiB
Markdown
## Maven
|
||
|
||

|
||
|
||

|
||
|
||
Maven仓库分为:
|
||
|
||
- 本地仓库:自己计算机上的一个目录(用来存储jar包)
|
||
- 中央仓库:由Maven团队维护的全球唯一的。仓库地址:https://repo1.maven.org/maven2/
|
||
- 远程仓库(私服):一般由公司团队搭建的私有仓库
|
||
|
||
POM文件导入依赖的时候,先看本地仓库有没有,没有就看私服,再没有就从中央仓库下载。
|
||
|
||
|
||
|
||
### Maven创建/导入项目
|
||
|
||
#### **创建Maven项目**
|
||
|
||

|
||
|
||
勾选 **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中的坐标是 == 资源的唯一标识 == 通过该坐标可以唯一定位资源位置
|
||
* 使用坐标来定义项目或引入项目中需要的依赖
|
||
|
||

|
||
|
||
### 依赖管理
|
||
|
||
可以到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刷新按钮,点击一下就开始联网下载依赖了,成功后可以看到
|
||
|
||

|
||
|
||
#### 排除依赖
|
||
|
||
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`。
|
||
|
||
**逐个执行**(默认是串行)所有这些编译后的测试类。 |