对于Maven依赖管理项目构建工具的介绍,我们将其分为上篇和下篇。如果您对文章感兴趣,您可以在此链接中找到上篇详细内容: Maven依赖管理项目构建工具(保姆级教学上篇)-CSDN博客
一、Maven依赖传递和依赖冲突
1. Maven依赖传递特性
概念
假如有Maven项目A,项目B依赖A,项目C依赖B。那么我们可以说 C依赖A。也就是说,依赖的关系为:C—>B—>A, 那么我们执行项目C时,会自动把B、A都下载导入到C项目的jar包文件夹中,这就是依赖的传递性。
作用
-
简化依赖导入过程
-
确保依赖版本正确
传递的原则
在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围以及配置
-
B 依赖 C 时使用 compile 范围:可以传递
-
B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。
-
B 依赖 C 时,若配置了以下标签<optional>,则不能传递
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
<optional>true</optional>
</dependency>
依赖传递终止
-
非compile范围进行依赖传递
-
使用optional配置终止传递
-
依赖冲突(传递的依赖已经存在)
2. Maven依赖冲突特性
当直接引用或者间接引用出现了相同的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)
在<depencies></depencies>中,先声明的,路径相同,会优先选择!
-
-
手动排除
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>pro01-maven-java</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
<!-- 使用excludes标签配置依赖的排除 -->
<exclusions>
<!-- 在exclude标签中配置一个具体的排除 -->
<exclusion>
<!-- 指定要排除的依赖的坐标(不需要写version) -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
二、Maven工程继承和聚合关系
1. Maven工程继承关系
-
继承概念
Maven 继承是指在 Maven 的项目中,让一个项目从另一个项目中继承配置信息的机制。继承可以让我们在多个项目中共享同一配置信息,简化项目的管理和维护工作。
-
继承作用
在父工程中统一管理项目中的依赖信息。
它的背景是:
-
对一个比较大型的项目进行了模块拆分。
-
一个 project 下面,创建了很多个 module。
-
每一个 module 都需要配置自己的依赖信息。
它背后的需求是:
-
在每一个 module 中各自维护各自的依赖信息很容易发生出入,不易统一管理。
-
使用同一个框架内的不同 jar 包,它们应该是同一个版本,所以整个项目中使用的框架版本需要统一。
-
使用框架时所需要的 jar 包组合(或者说依赖信息组合)需要经过长期摸索和反复调试,最终确定一个可用组合。这个耗费很大精力总结出来的方案不应该在新的项目中重新摸索。 通过在父工程中为整个项目维护依赖信息的组合既保证了整个项目使用规范、准确的 jar 包;又能够将以往的经验沉淀下来,节约时间和精力。
-
-
继承语法
-
父工程
<groupId>com.atguigu.maven</groupId> <artifactId>pro03-maven-parent</artifactId> <version>1.0-SNAPSHOT</version> <!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom --> <packaging>pom</packaging>
-
子工程
<!-- 使用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> -->
-
-
父工程依赖统一管理
-
父工程声明版本
<!-- 使用dependencyManagement标签配置对依赖的管理 --> <!-- 被管理的依赖并没有真正被引入到工程 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>6.0.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>6.0.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>6.0.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>6.0.10</version> </dependency> </dependencies> </dependencyManagement>
-
子工程引用版本
<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。 --> <!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 --> <!-- 具体来说是由父工程的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>
-
在 Maven 中,dependencyManagement
和 dependencies
元素通常都用于描述项目需要的依赖项,但它们在功能和使用时有所不同:
-
dependencyManagement
: 主要用于声明项目需要使用的依赖项的版本号和相关的属性信息,但实际并不会引入这些依赖项或者传递它们。即,dependencyManagement
主要是用于统一管理同一版本的依赖项,确保子模块和其他项目中的依赖项可以在同一版本上协同工作。如果一个子模块需要使用某个被声明在dependencyManagement
中的依赖项,则可以直接引用该依赖项而无需指定版本号。使用dependencyManagement
可以让项目的依赖管理更加清晰和简单。 -
dependencies
: 用于直接声明项目需要使用的依赖项,并将这些依赖项及其直接依赖项传递到项目中。当 Maven 构建项目时,会自动下载这些依赖项并构建出项目所需的类路径等信息。常用的依赖范围有compile
、test
、provided
、runtime
等。
因此,dependencyManagement
和 dependencies
元素在 Maven 中常常搭配使用。dependencyManagement
用于管理依赖项的版本和属性信息,而 dependencies
则用于将所需的依赖项引入并传递到项目中。
2. Maven工程聚合关系
-
聚合概念
Maven 聚合是指将多个项目组织到一个父级项目中,以便一起构建和管理的机制。聚合可以帮助我们更好地管理一组相关的子项目,同时简化它们的构建和部署过程。
-
聚合作用
-
管理多个子项目:通过聚合,可以将多个子项目组织在一起,方便管理和维护。
-
构建和发布一组相关的项目:通过聚合,可以在一个命令中构建和发布多个相关的项目,简化了部署和维护工作。
-
优化构建顺序:通过聚合,可以对多个项目进行顺序控制,避免出现构建依赖混乱导致构建失败的情况。
-
统一管理依赖项:通过聚合,可以在父项目中管理公共依赖项和插件,避免重复定义。
-
-
聚合语法
父项目中包含的子项目列表。
<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>
-
聚合演示
通过触发父工程构建命令、引发所有子模块构建!产生反应堆!
三、Maven私服
1. Maven私服简介
①私服简介
Maven 私服是一种特殊的Maven远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(中央仓库、其他远程公共仓库)。
当然也并不是说私服只能建立在局域网,也有很多公司会直接把私服部署到公网,具体还是得看公司业务的性质是否是保密的等等,因为局域网的话只能在公司用,部署到公网的话员工在家里也可以办公使用。
建立了 Maven 私服后,当局域网内的用户需要某个构件时,会按照如下顺序进行请求和下载。
请求本地仓库,若本地仓库不存在所需构件,则跳转到第 2 步; 请求 Maven 私服,将所需构件下载到本地仓库,若私服中不存在所需构件,则跳转到第 3 步。 请求外部的远程仓库,将所需构件下载并缓存到 Maven 私服,若外部远程仓库不存在所需构件,则 Maven 直接报错。
此外,一些无法从外部仓库下载到的构件,也能从本地上传到私服供其他人使用。
②Maven私服的优势
-
节省外网带宽 消除对外部远程仓库的大量重复请求(会消耗很大量的带宽),降低外网带宽压力。
-
下载速度更快 Maven私服位于局域网内,从私服下载构建更快更稳定。
-
便于部署第三方构件 有些构件无法从任何一个远程仓库中获得(如:公司或组织内部的私有构件、Oracle的JDBC驱动等),建立私服之后,就可以将这些构件部署到私服中,供内部Maven项目使用。
-
提高项目的稳定性,增强对项目的控制 如果不建立私服,那么Maven项目的构件就高度依赖外部的远程仓库,若外部网络不稳定,则项目的构建过程也会变得不稳定。建立私服后,即使外部网络状况不佳甚至中断,只要私服中已经缓存了所需的构件,Maven也能够正常运行。私服软件(如:Nexus)提供了很多控制功能(如:权限管理、RELEASE/SNAPSHOT版本控制等),可以对仓库进行一些更加高级的控制。
-
降低中央仓库得负荷压力 由于私服会缓存中央仓库得构件,避免了很多对中央仓库的重复下载,降低了中央仓库的负荷。
③常见的Maven私服产品
-
Apache的Archiva
-
JFrog的Artifactory
-
Sonatype的Nexus([ˈneksəs])(当前最流行、使用最广泛)
2. Nexus下载安装
下载地址:Download
解压,以管理员身份打开CMD,进入bin目录下,执行./nexus /run命令启动
访问 Nexus 首页
首页地址:http://localhost:8081/,8081为默认端口号
3. 初始设置
这里参考提示:
-
用户名:admin
-
密码:查看 E:\Server\nexus-3.61.0-02-win64\sonatype-work\nexus3\admin.password 文件
继续执行初始化:
匿名登录,启用还是禁用?由于启用匿名登录后,后续操作比较简单,这里我们演示禁用匿名登录的操作:
初始化完毕:
4. Nexus上的各种仓库
仓库类型 | 说明 |
---|---|
proxy | 某个远程仓库的代理 |
group | 存放:通过 Nexus 获取的第三方 jar 包 |
hosted | 存放:本团队其他开发人员部署到 Nexus 的 jar 包 |
仓库名称 | 说明 |
---|---|
maven-central | Nexus 对 Maven 中央仓库的代理 |
maven-public | Nexus 默认创建,供开发人员下载使用的组仓库 |
maven-releases | Nexus 默认创建,供开发人员部署自己 jar 包的宿主仓库 要求 releases 版本 |
maven-snapshots | Nexus 默认创建,供开发人员部署自己 jar 包的宿主仓库 要求 snapshots 版本 |
初始状态下,这几个仓库都没有内容:
5. 通过 Nexus 下载 jar 包
修改本地maven的核心配置文件settings.xml,设置新的本地仓库地址
<!-- 配置一个新的 Maven 本地仓库 -->
<localRepository>D:/maven-repository-new</localRepository>
把我们原来配置阿里云仓库地址的 mirror 标签改成下面这样:
<mirror>
<id>nexus-mine</id>
<mirrorOf>central</mirrorOf>
<name>Nexus mine</name>
<url>http://localhost:8081/repository/maven-public/</url>
</mirror>
这里的 url 标签是这么来的:
把上图中看到的地址复制出来即可。如果我们在前面允许了匿名访问,到这里就够了。但如果我们禁用了匿名访问,那么接下来我们还要继续配置 settings.xml:
<server>
<id>nexus-mine</id>
<username>admin</username>
<password>atguigu</password>
</server>
这里需要格外注意:server 标签内的 id 标签值必须和 mirror 标签中的 id 值一样。
找一个用到框架的 Maven 工程,执行命令:
mvn clean compile
下载过程日志:
下载后,Nexus 服务器上就有了 jar 包:
若下载速度太慢,可以设置私服中中央仓库的地址为阿里云仓库地址
修改为:http://maven.aliyun.com/nexus/content/groups/public/
6. 将 jar 包部署到 Nexus
maven工程中配置:
<distributionManagement>
<snapshotRepository>
<id>nexus-mine</id>
<name>Nexus Snapshot</name>
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
注意:这里 snapshotRepository 的 id 标签必须和 settings.xml 中指定的 mirror 标签的 id 属性一致。
执行部署命令:
mvn deploy
7. 引用别人部署的 jar 包
maven工程中配置:
<repositories>
<repository>
<id>nexus-mine</id>
<name>nexus-mine</name>
<url>http://localhost:8081/repository/maven-snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>