Maven简介、安装、使用、依赖传递

Maven

0 前言

本篇博客是通过b站尚硅谷2022版Maven教程学习所得
未完成的后续会看完补上。。待续

1 为什么要使用Maven

  • maven可以作为依赖管理工具
    • 项目中需要引入的各种jar包非常多,自己管理非常麻烦;有了maven可以直接在pom文件中,写入对应的依赖项,maven自动帮我们配置下载
    • jar包的下载也没有那么方便,而使用 Maven 后,可以根据具体的名称、版本、具体细节依赖对应的 jar 包,能够自动下载,方便、快捷又规范
    • jar包之间的依赖关系也是非常复杂的,有时也会出现一些冲突;而maven可以帮我们管理这些依赖

2 什么是Maven

Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供构建依赖管理支持的工具。
在这里插入图片描述

2.1 构建

  • 构建过程包含的主要的环节:
    • 清理:删除上一次构建的结果,为下一次构建做好准备
    • 编译:Java 源程序编译成 *.class 字节码文件
    • 测试:运行提前准备好的测试程序
    • 报告:针对刚才测试的结果生成一个全面的信息
    • 打包
      • Java工程:jar包
      • Web工程:war包
    • 安装:把一个 Maven 工程经过打包操作生成的 jar 包或 war 包存入 Maven 仓库
    • 部署
      • 部署 jar 包:把一个 jar 包部署到 Nexus 私服服务器上
      • 部署 war 包:借助相关 Maven 插件(例如 cargo),将 war 包部署到 Tomcat 服务器上

2.2 依赖

  • 如果 A 工程里面用到了 B 工程的类、接口、配置文件等等这样的资源,那么我们就可以说 A 依赖 B
  • 依赖管理中要解决的具体问题:
    • jar 包的下载:使用 Maven 之后,jar 包会从规范的远程仓库下载到本地
    • jar 包之间的依赖:通过依赖的传递性自动完成
    • jar 包之间的冲突:通过对依赖的配置进行调整,让某些jar包不会被导入

3 maven的工作机制

在这里插入图片描述

4 Maven的安装与配置

4.1 官网地址下载

Maven官网链接
在这里插入图片描述
因为我是在linux服务器上使用,所以下载如下
在这里插入图片描述

4.2 环境变量的配置

我是在/etc/profile.d/myenv.sh新建了一个myenv.sh专门用于添加我安装软件的环境变量
也可以直接在/etc/profile中添加

sudo vim /etc/profile.d/myenv.sh
# 添加如下
	# MAVEN_HOME
	export MAVEN_HOME=/opt/apache-maven-3.8.6
	export PATH=$PATH:$MAVEN_HOME/bin

source /etc/profile # 环境变量就生效了

需要注意的是,安装maven之前必须安装java

4.3 maven本地仓库和镜像的配置

  • maven的目录
drwxr-xr-x 2 nowcoder1 nowcoder  4096 Oct  1 17:09 bin
drwxr-xr-x 2 nowcoder1 nowcoder  4096 Oct  1 17:09 boot
drwxr-xr-x 3 nowcoder1 nowcoder  4096 Oct 16 23:16 conf
drwxr-xr-x 4 nowcoder1 nowcoder  4096 Oct  1 17:09 lib
-rw-r--r-- 1 nowcoder1 nowcoder 17568 Jun  7 00:16 LICENSE
-rw-r--r-- 1 nowcoder1 nowcoder  5141 Jun  7 00:16 NOTICE
-rw-r--r-- 1 nowcoder1 nowcoder  2612 Jun  7 00:16 README.txt
  • 进入conf目录下
drwxr-xr-x 2 nowcoder1 nowcoder  4096 Jun  7 00:16 logging
-rw-r--r-- 1 nowcoder1 nowcoder 10702 Oct 16 23:16 settings.xml
-rw-r--r-- 1 nowcoder1 nowcoder  3747 Jun  7 00:16 toolchains.xml
  • 修改settings.xml
    • 设置本地仓库位置

<!-- 设置本地仓库位置 -->
<localRepository>D:\maven-repository</localRepository>
    • 设置远程仓库镜像
      • Maven 下载 jar 包默认访问境外的中央仓库,而国外网站速度很慢。改成阿里云提供的镜像仓库,访问国内网站,可以让 Maven 下载 jar 包的时候速度更快
<mirrors>
    <!-- mirror
     | Specifies a repository mirror site to use instead of a given repository. The repository that
     | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
     | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
     |
    <mirror>
      <id>mirrorId</id>
      <mirrorOf>repositoryId</mirrorOf>
      <name>Human Readable Name for this Mirror.</name>
      <url>http://my.repository.com/repo/path</url>
    </mirror>
     -->
    <mirror>
      <id>aliyunmaven</id>
      <mirrorOf>*</mirrorOf>
      <name>阿里云公共仓库</name>
      <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
  </mirrors>

5 Maven中jar包的存储路径

5.1 Maven中的坐标

[1] 说明

使用三个『向量』在『Maven的仓库』中唯一的定位到一个『jar』包。

  • groupId:公司或组织的 id
  • artifactId:一个项目或者是项目中的一个模块的 id
  • version:版本号

[2] 取值方式

  • groupId:公司或组织域名的倒序,通常也会加上项目名称
    • 例如:com.atguigu.maven
  • artifactId:模块的名称,将来作为 Maven 工程的工程名
  • version:模块的版本号,根据自己的需要设定
    • 例如:SNAPSHOT 表示快照版本,正在迭代过程中,不稳定的版本
    • 例如:RELEASE 表示正式版本

[3] 坐标和仓库中 jar 包的存储路径之间的对应关系

坐标

<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>

对应的仓库存储路径

Maven本地仓库根目录\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar

6 Maven核心概念:POM

①含义

POM:Project Object Model,项目对象模型。和 POM 类似的是:DOM(Document Object Model),文档对象模型。它们都是模型化思想的具体体现。

②模型化思想

POM 表示将工程抽象为一个模型,再用程序中的对象来描述这个模型。这样我们就可以用程序来管理项目了。我们在开发过程中,最基本的做法就是将现实生活中的事物抽象为模型,然后封装模型相关的数据作为一个对象,这样就可以在程序中计算与现实事物相关的数据。

③对应的配置文件

POM 理念集中体现在 Maven 工程根目录下 pom.xml 这个配置文件中。所以这个 pom.xml 配置文件就是 Maven 工程的核心配置文件。其实学习 Maven 就是学这个文件怎么配置,各个配置有什么用。

7 Maven核心概念:约定的目录结构

①各个目录的作用

另外还有一个 target 目录专门存放构建操作输出的结果。
在这里插入图片描述

②约定目录结构的意义

Maven 为了让构建过程能够尽可能自动化完成,所以必须约定目录结构的作用。

  • 例如:Maven 执行编译操作,必须先去 Java 源程序目录读取 Java 源代码,然后执行编译,最后把编译结果存放在 target 目录。

③约定大于配置

Maven 对于目录结构这个问题,没有采用配置的方式,而是基于约定。这样会让我们在开发过程中非常方便。如果每次创建 Maven 工程后,还需要针对各个目录的位置进行详细的配置,那肯定非常麻烦。

目前开发领域的技术发展趋势就是:约定大于配置,配置大于编码

8 Maven的一些常用命令

8.1 执行命令前的要求

运行 Maven 中和构建操作相关的命令时,必须进入到 pom.xml 所在的目录

8.2 清理操作

mvn clean # 效果:删除 target 目录

8.3 编译操作

# 主程序编译:
mvn compile

# 测试程序编译:
mvn test-compile

# 主体程序编译结果存放的目录:
target/classes

# 测试程序编译结果存放的目录:
target/test-classes

8.4 测试操作

mvn package

# 打包的结果——jar 包,存放的目录:target

8.5 安装操作

mvn install

安装的效果是

  • 将本地构建过程中生成的 jar 包存入 Maven 本地仓库。
  • 这个 jar 包在 Maven 仓库中的路径是根据它的坐标生成的
  • 然后其他的项目也可以使用这个jar包

安装操作还会将 pom.xml 文件转换为 XXX.pom 文件一起存入本地仓库。

  • 所以我们在 Maven 的本地仓库中想看一个 jar 包原始的 pom.xml 文件时,查看对应 XXX.pom 文件即可,它们是名字发生了改变,本质上是同一个文件。

9 Maven中的依赖

9.1 查看依赖的jar包命令

mvn dependency:list # 列表形显示
mvn dependency:tree # 树形显示

9.2 依赖的范围

  • 标签的位置:dependencies/dependency/scope
  • 标签的可选值:compile/test/provided/system/runtime/import
①compile 和 test 对比
main目录(空间)test目录(空间)开发过程(时间)部署到服务器(时间)
compile有效有效有效有效
test无效有效有效无效
②compile 和 provided 对比
main目录(空间)test目录(空间)开发过程(时间)部署到服务器(时间)
compile有效有效有效有效
provided有效有效有效无效
③结论

[1] compile

  • 通常使用的第三方框架的 jar 包这样在项目实际运行时真正要用到的 jar 包都是以 compile 范围进行依赖的。比如 SSM 框架所需jar包。

[2] test

  • 测试过程中使用的 jar 包,以 test 范围依赖进来。比如 junit。

[3] provided

  • 在开发过程中需要用到的“服务器上的 jar 包”通常以 provided 范围依赖进来。
  • 比如 servlet-api、jsp-api。而这个范围的 jar 包之所以不参与部署、不放进 war 包,就是避免和服务器上已有的同类 jar 包产生冲突,同时减轻服务器的负担。说白了就是:“服务器上已经有了,你就别带啦!”

9.3 依赖补充

①import

管理依赖最基本的办法是继承父工程,但是和 Java 类一样,Maven 也是单继承的。如果不同体系的依赖信息封装在不同 POM 中了,没办法继承多个父工程怎么办?这时就可以使用 import 依赖范围

②system

以 Windows 系统环境下开发为例,假设现在 D:\tempare\atguigu-maven-test-aaa-1.0-SNAPSHOT.jar 想要引入到我们的项目中,此时我们就可以将依赖配置为 system 范围:

<dependency>
    <groupId>com.atguigu.maven</groupId>
    <artifactId>atguigu-maven-test-aaa</artifactId>
    <version>1.0-SNAPSHOT</version>
    <systemPath>D:\tempare\atguigu-maven-test-aaa-1.0-SNAPSHOT.jar</systemPath>
    <scope>system</scope>
</dependency>

但是很明显:这样引入依赖完全不具有可移植性,所以不要使用。如果需要引入体系外 jar 包我们后面会讲专门的办法。

③runtime

专门用于编译时不需要,但是运行时需要的 jar 包。

9.4 依赖的传递性

1、依赖的传递性
①概念

A 依赖 B,B 依赖 C,那么在 A 没有配置对 C 的依赖的情况下,A 里面能不能直接使用 C?

②传递的原则

在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围。

  • B 依赖 C 时使用 compile 范围:可以传递
  • B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。
2、依赖传递测试
  • 测试方式:
    • pro03-maven-web在pom.xml中已经依赖pro01-maven-java了
    • 现在我们让 pro01-maven-java 工程依赖三个不同范围的
  • 具体操作:编辑 pro01-maven-java 工程根目录下 pom.xml
  • 注意这里,pro01-maven-java 修改pom之后要mvn install一下
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
	<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.58</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
    <scope>provided</scope>
</dependency>

测试结果

  • pro01-maven-java的依赖
[INFO] com.atguigu.maven:pro01-maven-java:jar:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-core:jar:4.0.0.RELEASE:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] +- com.alibaba:fastjson:jar:1.2.58:test
[INFO] +- mysql:mysql-connector-java:jar:8.0.16:provided
[INFO] |  \- com.google.protobuf:protobuf-java:jar:3.6.1:provided
[INFO] \- junit:junit:jar:4.12:test
[INFO]    \- org.hamcrest:hamcrest-core:jar:1.3:test
  • pro03-maven-web的依赖
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ pro03-maven-web ---
[INFO] com.atguigu.maven:pro03-maven-web:war:1.0-SNAPSHOT
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] +- com.atguigu.maven:pro01-maven-java:jar:1.0-SNAPSHOT:compile
[INFO] |  \- org.springframework:spring-core:jar:4.0.0.RELEASE:compile
[INFO] |     \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] \- junit:junit:jar:4.11:test
[INFO]    \- org.hamcrest:hamcrest-core:jar:1.3:test
  • 结论
pro01-maven-java
依赖的包
依赖的范围pro03-maven-web结果
spring-corecompile
fastjsontest没有
mysql-connector-javaprovided没有
3、依赖的排除
① 概念

当 A 依赖 B,B 依赖 C 而且 C 可以传递到 A 的时候,A 不想要 C,需要在 A 里面把 C 排除掉。而往往这种情况都是为了避免 jar 包之间的冲突。
在这里插入图片描述
所以配置依赖的排除其实就是阻止某些 jar 包的传递。因为这样的 jar 包传递过来会和其他 jar 包冲突

②测试
  • 方法:在 pro02-maven-web 工程中配置对 commons-logging 的排除
<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>
  • 配置前
[INFO] 1:pro02-maven-web:war:1.0-SNAPSHOT
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] +- com.atguigu.maven:pro01-maven-java:jar:1.0-SNAPSHOT:compile
[INFO] |  \- org.springframework:spring-core:jar:4.0.0.RELEASE:compile
[INFO] |     \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] \- junit:junit:jar:4.11:test
[INFO]    \- org.hamcrest:hamcrest-core:jar:1.3:test
  • 配置后
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ pro02-maven-web ---
[INFO] 1:pro02-maven-web:war:1.0-SNAPSHOT
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] +- com.atguigu.maven:pro01-maven-java:jar:1.0-SNAPSHOT:compile
[INFO] |  \- org.springframework:spring-core:jar:4.0.0.RELEASE:compile
[INFO] \- junit:junit:jar:4.11:test
[INFO]    \- org.hamcrest:hamcrest-core:jar:1.3:test

9.5 依赖配置

1、可选依赖

true

可选其实就是『可有可无』。官网的解释是:
在这里插入图片描述
其核心含义是:Project X 依赖 Project A,A 中一部分 X 用不到的代码依赖了 B,那么对 X 来说 B 就是『可有可无』的。
在这里插入图片描述

2 版本仲裁
①最短路径优先

在下图的例子中,对模块 pro25-module-a 来说,Maven 会采纳 1.2.12 版本。
在这里插入图片描述

②路径相同时先声明者优先

此时 Maven 采纳哪个版本,取决于在 pro29-module-x 中,对 pro30-module-y 和 pro31-module-z 两个模块的依赖哪一个先声明。
在这里插入图片描述

③小结
  • 其实 Maven 的版本仲裁机制只是在没有人为干预的情况下,自主决定 jar 包版本的一个办法。
  • 而实际上我们要使用具体的哪一个版本,还要取决于项目中的实际情况。
  • 所以在项目正常运行的情况下,jar 包版本可以由 Maven 仲裁,不必我们操心;
  • 而发生冲突时 Maven 仲裁决定的版本无法满足要求,此时就应该由程序员明确指定 jar 包版本。

10 Maven的几个打包命令

  • package命令:完成项目编译、单元测试、打包功能,但打包文件未部署到本地Maven仓库和远程Maven仓库。
  • install命令:完成项目编译、单元测试、打包功能,同时把打包文件部署到本地Maven仓库,但未部署到远程Maven仓库。
  • deploy命令:完成项目编译、单元测试、打包功能,同时把打包文件部署到本地Maven仓库和远程Maven仓库。
  • 注意:
    maven下载jar包时就存储在仓库里面了,而以下操作是把整个项目打包成一个jar包中存储在仓库中

11 Maven中的继承

1、概念

Maven工程之间,A 工程继承 B 工程

  • B 工程:父工程
  • A 工程:子工程
    本质上是 A 工程的 pom.xml 中的配置继承了 B 工程中 pom.xml 的配置

2、作用

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

它的背景是:

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

它背后的需求是:

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

3、操作

①创建父工程

创建的过程:pro03-maven-parent

工程创建好之后,要修改它的打包方式:

<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>

<!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
<packaging>pom</packaging>
  • 只有打包方式为 pom 的 Maven 工程能够管理其他 Maven 工程
  • 打包方式为 pom 的 Maven 工程中不写业务代码,它是专门管理其他 Maven 工程的工程。
②创建模块工程

模块工程类似于 IDEA 中的 module,所以需要进入 pro03-maven-parent 工程的根目录,然后运行 mvn archetype:generate 命令来创建模块工程。
我们创建三个模块工程:

  • pro04-maven-module
  • pro05-maven-module
  • pro06-maven-module
③查看被添加新内容的父工程 pom.xml

下面 modules 和 module 标签是聚合功能的配置

<modules>  
	<module>pro04-maven-module</module>
	<module>pro05-maven-module</module>
	<module>pro06-maven-module</module>
</modules>
④解读子工程的pom.xml
<!-- 使用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>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>
⑥子工程中引用那些被父工程管理的依赖

关键点:省略版本号

<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。	-->
<!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
<!-- 具体来说是由父工程的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>
⑦在父工程中升级依赖信息的版本
<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-beans</artifactId>
				<version>4.1.4.RELEASE</version>
</dependency>

mvn dependency:list

[INFO] org.springframework:spring-aop:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-core:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-context:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-beans:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-expression:jar:4.1.4.RELEASE:compile
⑧在父工程中声明自定义属性
<!-- 通过自定义属性,统一指定Spring的版本 -->
<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	
	<!-- 自定义标签,维护Spring版本数据 -->
	<atguigu.spring.version>4.3.6.RELEASE</atguigu.spring.version>
</properties>

在需要的地方使用${}的形式来引用自定义的属性名:

<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-core</artifactId>
				<version>${atguigu.spring.version}</version>
</dependency>

真正实现“一处修改,处处生效”。

4、实际意义

在这里插入图片描述
编写一套符合要求、开发各种功能都能正常工作的依赖组合并不容易。如果公司里已经有人总结了成熟的组合方案,那么再开发新项目时,如果不使用原有的积累,而是重新摸索,会浪费大量的时间。为了提高效率,我们可以使用工程继承的机制,让成熟的依赖组合方案能够保留下来。

如上图所示,公司级的父工程中管理的就是成熟的依赖组合方案,各个新项目、子系统各取所需即可。

12 聚合

1、聚合本身的含义

部分组成整体
在这里插入图片描述

2、Maven 中的聚合

使用一个“总工程”将各个“模块工程”汇集起来,作为一个整体对应完整的项目。

  • 项目:整体
  • 模块:部分

概念的对应关系:

  • 从继承关系角度来看:
    • 父工程
    • 子工程
  • 从聚合关系角度来看:
    • 总工程
    • 模块工程

3、好处

  • 一键执行 Maven 命令:很多构建命令都可以在“总工程”中一键执行。
    • 以 mvn install 命令为例:Maven 要求有父工程时先安装父工程;
    • 有依赖的工程时,先安装被依赖的工程。我们自己考虑这些规则会很麻烦。
    • 但是工程聚合之后,在总工程执行 mvn install 可以一键完成安装,而且会自动按照正确的顺序执行。
  • 配置聚合之后,各个模块工程会在总工程中展示一个列表,让项目中的各个模块一目了然。

4、聚合的配置

在总工程中配置 modules 即可:

<modules>  
		<module>pro04-maven-module</module>
		<module>pro05-maven-module</module>
		<module>pro06-maven-module</module>
</modules>

5、依赖循环问题

如果 A 工程依赖 B 工程,B 工程依赖 C 工程,C 工程又反过来依赖 A 工程,那么在执行构建操作时会报下面的错误:

DANGER
[ERROR] [ERROR] The projects in the reactor contain a cyclic reference:

12 Maven其他核心概念

1、生命周期

①作用

为了让构建过程自动化完成,Maven 设定了三个生命周期,生命周期中的每一个环节对应构建过程中的一个操作。

②三个生命周期
生命周期名称作用各个环节
Clean清理操作相关
pre-clean
clean
post-clean
Site生成站点相关
pre-site
site
post-site
deploy-site
Default主要构建过程
validate
generate-sources
process-sources
generate-resources
process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目 main 目录下的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码。
process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package
package 接受编译好的代码,打包成可发布的格式,如JAR。
pre-integration-test
integration-test
post-integration-test
verify
install将包安装至本地仓库,以让其它项目依赖。
deploy将最终的包复制到远程的仓库,以让其它开发人员共享;或者部署到服务器上运行(需借助插件,例如:cargo)。
③特点
  • 前面三个生命周期彼此是独立的。
  • 在任何一个生命周期内部,执行任何一个具体环节的操作,都是从本周期最初的位置开始执行,直到指定的地方。(本节记住这句话就行了,其他的都不需要记)

Maven 之所以这么设计其实就是为了提高构建过程的自动化程度:让使用者只关心最终要干的即可,过程中的各个环节是自动执行的。

2、插件和目标

①插件

Maven 的核心程序仅仅负责宏观调度,不做具体工作。具体工作都是由 Maven 插件完成的。例如:编译就是由 maven-compiler-plugin-3.1.jar 插件来执行的。

②目标

一个插件可以对应多个目标,而每一个目标都和生命周期中的某一个环节对应。

Default 生命周期中有 compile 和 test-compile 两个和编译相关的环节,这两个环节对应 compile 和 test-compile 两个目标,而这两个目标都是由 maven-compiler-plugin-3.1.jar 插件来执行的。

3、仓库

  • 本地仓库:在当前电脑上,为电脑上所有 Maven 工程服务
  • 远程仓库:需要联网
    • 局域网:我们自己搭建的 Maven 私服,例如使用 Nexus 技术。
    • Internet
      • 中央仓库
      • 镜像仓库:内容和中央仓库保持一致,但是能够分担中央仓库的负载,同时让用户能够就近访问提高下载速度,例如:Nexus aliyun

建议:不要中央仓库和阿里云镜像混用,否则 jar 包来源不纯,彼此冲突

专门搜索 Maven 依赖信息的网站:https://mvnrepository.com/

13 POM 的四个层次

1、超级 POM

Maven 在构建过程中有很多默认的设定。例如:源文件存放的目录、测试源文件存放的目录、构建输出的目录……等等。但是其实这些要素也都是被 Maven 定义过的。定义的位置就是:超级 POM

关于超级 POM,Maven 官网是这样介绍的:

The Super POM is Maven’s default POM. All POMs extend the Super POM unless explicitly set, meaning the configuration specified in the Super POM is inherited by the POMs you created for your projects.
译文:Super POM 是 Maven 的默认 POM。除非明确设置,否则所有 POM 都扩展 Super POM,这意味着 Super POM 中指定的配置由您为项目创建的 POM 继承。

所以我们自己的 POM 即使没有明确指定一个父工程(父 POM),其实也默认继承了超级 POM。就好比一个 Java 类默认继承了 Object 类。

2、父 POM

和 Java 类一样,POM 之间其实也是单继承的。如果我们给一个 POM 指定了父 POM,那么继承关系如下图所示:
在这里插入图片描述

3、有效POM

①概念

有效 POM 英文翻译为 effective POM,它的概念是这样的——在 POM 的继承关系中,子 POM 可以覆盖父 POM 中的配置;如果子 POM 没有覆盖,那么父 POM 中的配置将会被继承。按照这个规则,继承关系中的所有 POM 叠加到一起,就得到了一个最终生效的 POM。显然 Maven 实际运行过程中,执行构建操作就是按照这个最终生效的 POM 来运行的。这个最终生效的 POM 就是有效 POM,英文叫effective POM。

②查看有效 POM

mvn help:effective-pom

4、小结

综上所述,平时我们使用和配置的 POM 其实大致是由四个层次组成的:

  • 超级 POM:所有 POM 默认继承,只是有直接和间接之分。
  • 父 POM:这一层可能没有,可能有一层,也可能有很多层。
  • 当前 pom.xml 配置的 POM:我们最多关注和最多使用的一层。
  • 有效 POM:隐含的一层,但是实际上真正生效的一层。

13 Maven自定义插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值