一、Maven的主要作用

  • 依赖管理
  • 项目构建

二、Maven工程构建

2.1梳理Maven工程GAVP属性

Maven工程相对之前的工程,多出一组gavp属性,gav需要我们在创建项目的时指定,p有默认值,后期通过配置文件修改。

Maven 中的 GAVP 是指 GroupId、ArtifactId、Version、Packaging 等四个属性的缩写,其中前三个是必要的,而 Packaging 属性为可选项。这四个属性主要为每个项目在maven仓库总做一个标识,类似人的《姓-名》。有了具体标识,方便maven软件对项目进行管理和互相引用!

GAV遵循一下规则:

1) GroupID 格式:com.{公司/BU }.业务线.[子业务线],最多 4 级。

说明:{公司/BU} 例如:alibaba/taobao/tmall/aliexpress 等 BU 一级;子业务线可选。

正例:com.taobao.tddl 或 com.alibaba.sourcing.multilang com.atguigu.java

2) ArtifactID 格式:产品线名-模块名。语义不重复不遗漏,先到仓库中心去查证一下。

正例:tc-client / uic-api / tair-tool / bookstore

3) Version版本号格式推荐:主版本号.次版本号.修订号 1.0.0 (非强制性的,各个公司要求不同)

1) 主版本号:当做了不兼容的 API 修改,或者增加了能改变产品方向的新功能。

2) 次版本号:当做了向下兼容的功能性新增(新增类、接口等)。

3) 修订号:修复 bug,没有修改方法签名的功能加强,保持 API 兼容性。

例如: 初始→1.0.0 修改bug → 1.0.1 功能调整 → 1.1.1等

Packaging定义规则:

指示将项目打包为什么类型的文件,idea根据packaging值,识别maven项目类型!

packaging 属性为 jar(默认值),代表普通的Java工程,打包以后是.jar结尾的文件。

packaging 属性为 war,代表Java的web工程,打包以后.war结尾的文件。

packaging 属性为 pom,代表不会打包,用来做继承的父工程。

2.2 Idea构建Maven JavaSE工程

注意:此处省略了version,直接给了一个默认值1.0-SNAPSHOT

2.3 Idea构建Maven JavaEE工程

  1. 手动创建

    1. 创建一个javasemaven工程

    2. 手动添加web项目结构文件

      注意:结构和命名固定

    3. 修改pom.xml文件打包方式

      pom.xml

      1
      2
      3
      4
      5
      <groupId>com.atguigu</groupId>
      <artifactId>maven_parent</artifactId>
      <version>1.0-SNAPSHOT</version>
      <!-- 新增一列打包方式packaging -->
      <packaging>war</packaging>
    4. 刷新和校验

项目的webapp文件夹出现小蓝点,代表成功!!

  1. 插件方式创建

    1. 安装插件JBLJavaToWeb

      file / settings / plugins / marketplace

    2. 创建一个javasemaven工程

    3. 右键、使用插件快速补全web项目

三、Maven核心功能依赖和构建管理

3.1依赖管理和配置

以下是

  • 方便版本号管理(在propertiess中自定义命名,建议是依赖.version)
  • scope的属性的用处
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!--声明版本-->
<properties>
<!--命名随便,内部制定版本号即可!-->
<junit.version>4.11</junit.version>
<!-- 也可以通过 maven规定的固定的key,配置maven的参数!如下配置编码格式!-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!--引用properties声明版本 -->
<version>${junit.version}</version>
<!--
生效范围
- compile :main目录 test目录 打包打包 [默认]
- provided:main目录 test目录 Servlet
- runtime: 打包运行 MySQL
- test: test目录 junit
-->
<scope>test</scope>
</dependency>
</dependencies>

3.2依赖传递和冲突

依赖传递指的是当一个模块或库 A 依赖于另一个模块或库 B,而 B 又依赖于模块或库 C,那么 A 会间接依赖于 C。这种依赖传递结构可以形成一个依赖树。当我们引入一个库或框架时,构建工具(如 Maven、Gradle)会自动解析和加载其所有的直接和间接依赖,确保这些依赖都可用。

依赖传递的作用是:

1. 减少重复依赖:当多个项目依赖同一个库时,Maven 可以自动下载并且只下载一次该库。这样可以减少项目的构建时间和磁盘空间。
2. 自动管理依赖: Maven 可以自动管理依赖项,使用依赖传递,简化了依赖项的管理,使项目构建更加可靠和一致。
3. 确保依赖版本正确性:通过依赖传递的依赖,之间都不会存在版本兼容性问题,确实依赖的版本正确性!

==依赖传递演示:==

项目中,需要导入jackson相关的依赖,通过之前导入经验,jackson需要导入三个依赖,分别为:

通过查看网站介绍的依赖传递特性:data-bind中,依赖其他两个依赖

最佳导入:直接可以导入data-bind,自动依赖传递需要的依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>

==依赖冲突演示:==

当直接引用或者间接引用出现了相同的jar包! 这时呢,一个项目就会出现相同的重复jar包,这就算作冲突!依赖冲突避免出现重复依赖,并且终止依赖传递!

maven自动解决依赖冲突问题能力,会按照自己的原则,进行重复依赖选择。同时也提供了手动解决的冲突的方式,不过不推荐!

解决依赖冲突(如何选择重复依赖)方式:

自动选择原则

  • 短路优先原则(第一原则)

​ A—>B—>C—>D—>E—>X(version 0.0.1)

​ A—>F—>X(version 0.0.2)

​ 则A依赖于X(version 0.0.2)。 ​

  • 依赖路径长度相同情况下,则“先声明优先”(第二原则)

      A—>E—>X(version 0.0.1)
    
      A—>F—>X(version 0.0.2)
    
      在<dependencies></dependencies>中,先声明的,路径相同,会优先选择!

    小思考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
前提:
A 1.1 -> B 1.1 -> C 1.1
F 2.2 -> B 2.2

pom声明:
F 2.2
A 1.1

B 2.2

最后只有
F 2.2
A 1.1
B 2.2

3.3 依赖导入失败场景和解决方案

在使用 Maven 构建项目时,可能会发生依赖项下载错误的情况,主要原因有以下几种:

1. 下载依赖时出现网络故障或仓库服务器宕机等原因,导致无法连接至 Maven 仓库,从而无法下载依赖。
2. 依赖项的版本号或配置文件中的版本号错误,或者依赖项没有正确定义,导致 Maven 下载的依赖项与实际需要的不一致,从而引发错误。
3. 本地 Maven 仓库或缓存被污染或损坏,导致 Maven 无法正确地使用现有的依赖项,并且也无法重新下载!一般访问顺序是先访问本地仓库,没有就去访问镜像,镜像将一份存到本地仓库内。(具体表现可能为:访问本地仓库没有,不访问阿里镜像的问题,本地仓库被污染。)

解决方案:

  1. 检查网络连接和 Maven 仓库服务器状态。

  2. 确保依赖项的版本号与项目对应的版本号匹配,并检查 POM 文件中的依赖项是否正确。

  3. 清除本地 Maven 仓库缓存(lastUpdated 文件),因为只要存在lastupdated缓存文件,刷新也不会重新下载。本地仓库中,根据依赖的gav属性依次向下查找文件夹,最终删除内部的文件,刷新重新下载即可!

    例如: pom.xml依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>

文件:

脚本使用:

1
2
3
4
5
6
7
8
9
@echo off
rem 这里写你的仓库路径
set REPOSITORY_PATH=D:\apache-maven-3.8.8-bin\repository
rem 正在搜索...
for /f "delims=" %%i in ('dir /b /s "%REPOSITORY_PATH%\*lastUpdated*"') do (
del /s /q %%i
)
rem 搜索完毕
pause

使用记事本打开将set REPOSITORY_PATH=D:改成你本地仓库地址即可! 点击运行脚本,即可自动清理本地错误缓存文件!!

(ps:如遇到无法运行bat文件的情况:可以试试打开cmd,然后拖动bat文件进去)

3.4 扩展构建管理和插件配置

构建概念:

项目构建是指将源代码、依赖库和资源文件等转换成可执行或可部署的应用程序的过程,在这个过程中包括编译源代码、链接依赖库、打包和部署等多个步骤。

主动触发场景:

  • 重新编译 : 编译不充分, 部分文件没有被编译!
  • 打包 : 独立部署到外部服务器软件,打包部署
  • 部署本地或者私服仓库 : maven工程加入到本地或者私服仓库,供其他工程使用

命令方式构建:

语法: mvn 构建命令 构建命令....

命令 描述
mvn clean 清理编译或打包后的项目结构,删除target文件夹
mvn compile 编译项目,生成target文件
mvn test 执行测试源码 (测试)
mvn site 生成一个项目依赖信息的展示页面
mvn package 打包项目,生成war / jar 文件
mvn install 打包后上传到maven本地仓库(本地部署)
mvn deploy 只打包,上传到maven私服仓库(私服部署)

可视化方式构建:

构建命令周期:

构建生命周期可以理解成是一组固定构建命令的有序集合,触发周期后的命令,会自动触发周期前的命令!也是一种简化构建的思路!

  • 清理周期:主要是对项目编译生成文件进行清理

    包含命令:clean

  • 默认周期:定义了真正构件时所需要执行的所有步骤,它是生命周期中最核心的部分

    包含命令:compile - test - package - install / deploy

  • 报告周期

    包含命令:site

    打包: mvn clean package 本地仓库: mvn clean install

最佳使用方案:

1
2
3
打包: mvn clean package
重新编译: mvn clean compile
本地部署: mvn clean install

周期,命令和插件:

周期→包含若干命令→包含若干插件!

使用周期命令构建,简化构建过程!

最终进行构建的是插件!

问题:

出现war包打包失败,可能是maven自带的war包打包插件版本太低,此时需要自己导入插件,在pom.xml中设置插件配置:

1
2
3
4
5
6
7
8
9
10
<build>
<!-- jdk17 和 war包版本插件不匹配 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
</plugins>
</build>

四、Maven继承和聚合特性

### 4.1 Maven工程继承关系

  1. 继承概念

    Maven 继承是指在 Maven 的项目中,让一个项目从另一个项目中继承配置信息的机制。继承可以让我们在多个项目中共享同一配置信息,简化项目的管理和维护工作。

  2. 继承作用

    作用:在父工程中统一管理项目中的依赖信息,进行统一版本管理!

    它的背景是:

    • 对一个比较大型的项目进行了模块拆分。
    • 一个 project 下面,创建了很多个 module。
    • 每一个 module 都需要配置自己的依赖信息。

    它背后的需求是:

    • 多个模块要使用同一个框架,它们应该是同一个版本,所以整个项目中使用的框架版本需要统一管理。
    • 使用框架时所需要的 jar 包组合(或者说依赖信息组合)需要经过长期摸索和反复调试,最终确定一个可用组合。这个耗费很大精力总结出来的方案不应该在新的项目中重新摸索。

    通过在父工程中为整个项目维护依赖信息的组合既保证了整个项目使用规范、准确的 jar 包;又能够将以往的经验沉淀下来,节约时间和精力。

  3. 继承语法

    在idea中创建一个一个父工程(module),然后右键在其上创建module,可以发现如下的parent和GroupId已经填好了。

    • 父工程

      1
      2
      3
      4
      5
      <groupId>com.atguigu.maven</groupId>
      <artifactId>pro03-maven-parent</artifactId>
      <version>1.0-SNAPSHOT</version>
      <!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
      <packaging>pom</packaging>
    • 子工程

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <!-- 使用parent标签指定当前工程的父工程 -->
      <parent>
      <!-- 父工程的坐标 -->
      <groupId>com.atguigu.maven</groupId>
      <artifactId>pro03-maven-parent</artifactId>
      <version>1.0-SNAPSHOT</version>
      </parent>

      <!-- 子工程的坐标 -->
      <!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
      <!-- <groupId>com.atguigu.maven</groupId> -->
      <artifactId>pro04-maven-module</artifactId>
      <!-- <version>1.0-SNAPSHOT</version> -->
  4. 父工程依赖统一管理

    • 父工程声明版本

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      <!-- 使用dependencyManagement标签配置对依赖的管理 -->
      <!-- 被管理的依赖并没有真正被引入到工程 -->
      <dependencyManagement>
      <dependencies>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.0.0.RELEASE</version>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.0.0.RELEASE</version>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.0.0.RELEASE</version>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>4.0.0.RELEASE</version>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.0.0.RELEASE</version>
      </dependency>
      </dependencies>
      </dependencyManagement>
    • 子工程引用版本

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。  -->
      <!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
      <!-- 具体来说是由父工程的dependencyManagement来决定。 -->
      <dependencies>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      </dependency>
      </dependencies>

### 4.2 Maven工程聚合关系

  1. 聚合概念

    Maven 聚合是指将多个项目组织到一个父级项目中,通过触发父工程的构建,统一按顺序触发子工程构建的过程!!

  2. 聚合作用

    • 统一管理子项目构建:通过聚合,可以将多个子项目组织在一起,方便管理和维护。
    • 优化构建顺序:通过聚合,可以对多个项目进行顺序控制,避免出现构建依赖混乱导致构建失败的情况。
  3. 聚合语法

    父项目中包含的子项目列表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <project>
    <groupId>com.example</groupId>
    <artifactId>parent-project</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0</version>
    <modules>
    <module>child-project1</module>
    <module>child-project2</module>
    </modules>
    </project>
  4. 聚合演示

    通过触发父工程构建命令、引发所有子模块构建!产生反应堆!