一、pom.xml文件
pom.xml文件是maven项目的核心。
POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖等。类似的产品还有Gredele,高级的项目大多使用Gredele管理,Gredele比Maven的坐标形式更简单。
以junit为例,
Maven坐标依赖

Gredele依赖

二、Maven坐标
1、坐标的概念
Maven中的坐标是构件的唯一标识符,坐标的元素包括groupId、artifactId、version、packaging、classifier。上述5个元素中,groupId、artifactId、version 是必须定义的,packaging 是可选的 ( 默认为jar )。pom包,没有任何代码的包,只做项目管理。还有war包。
2、坐标的意义
Maven使用统一的坐标规范将查找依赖资源的工作交给机器处理。
3、坐标的含义
groupId:组织标识,一般为公司网址的反写+项目名称
artifactId:项目名称,一般为:项目名_模块名
version:版本号,形式为0.0.0-XXXX:
版本号从左往右依次变小
XXXX: SNAPSHOT -- 快照版本,ALPHA -- 内侧版本,BETA -- 公测版本,RELEASE -- 稳定版本,GA -- 正式发布
packaging:打包的方式,如:pom, jar, war ,maven-plugin, ejb, ...
clissifier:用来帮助定义构件输出的一些附属构件。
4、自己项目的坐标
XML
<?xml version="1.0" encoding="UTF-8"?>
<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>
<!--项目的全球唯一标识符,通常使用全限定的包名区分该项目和其他项目。
并且构建时生成的路径也是由此生成, 如com.qcby生成的相对路径为:/com/qcby-->
<groupId>com.qcby</groupId>
<!-- 构件的标识符,它和 groupId 一起唯一标识一个构件。
也就是说,你不能有两个不同的项目拥有同样的 artifactId 和 groupId-->
<artifactId>hami_parent</artifactId>
<!--项目当前版本,格式为:主版本.次版本.增量版本-限定版本号-->
<packaging>pom</packaging>
<!--项目产生的构件类型,例如 jar、war、pom 等。插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型-->
<version>1.0-SNAPSHOT</version>
<!--继承于该项目的项目-->
<modules>
<module>hami_core</module>
<module>hami_console</module>
<module>hami_protoal</module>
<module>hami_photo</module>
</modules>
<!--父类项目的依赖管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
5、第三方项目的坐标
使用Hello模拟个人写的依赖包,junit是第三方依赖包

三、依赖
1、为什么需要依赖
当编写Java代码时,我们总是需要一些库,例如,做单元测试我们需要JUnit库。对于更大的项目,我们可能需要创建自己的库并在不同的部分使用它的项目。不同的项目需要不同版本的库。保持项目最新的库JAR文件的正确版本不是一个容易的任务。
每个外部JAR可能还依赖于其他外部JAR文件等。以递归方式下载所有这些外部依赖JAR文件并确保下载正确的版本是一项巨大的任务。
当项目越来越大,我们将需要越来越多的外部依赖。
Maven将下载它们并将它们放在您的本地Maven存储库中。
我们可以在POM文件中的dependencies元素内指定依赖关系。
2、使用依赖
例如我们的项目需要进行单元测试,则需要使用到junit-4.13.1.jar包,使用maven引用该依赖的方式如下:

属性说明:
三维坐标:引用依赖包的三维坐标,用来定位该依赖包;
scope: 控制该依赖包在什么情况下会被加到 classpath 中,即生效范围,可在编译期、测试期、运行时生效
3、第三方依赖的查找方法
我们在不确定所需引用的第三方依赖包的坐标时,通过maven的中央仓库进行查找,网址: https://mvnrepository.com/



刷新进行第三方库的导入,第一次使用会下载到本地仓库中, 随后的使用直接使用本地仓库中的资源

如果是2021或更高版本的IDEA,点开pom.xml文件时,右上角会有一个蓝色的m进行资源刷新。

4、依赖范围
Maven项目在开发工程中有三套classpath
主代码:main下面的都是主代码在编译的时候的依赖
测试代码:test下是测试代码编译的时候的依赖
运行时:main代码在运行的时候对包的依赖
依赖范围的使用,通过在引用第三方依赖时的标签进行设置,例如:

共 6 种 scope,包括:compile、provided、runtime、test、system、import。例如上图的junit,只在测试中使用,则选择test即可,默认为compile
Compile:编译依赖范围。默认使用此依赖范围,其下的maven依赖,对于编译,测试,运行classpath都有效。
Test:测试依赖范围。只对测试classpath有效,编译主代码或运行项目时无法使用此依赖。典型例子如junit。
Provided:已提供依赖范围。其对于编译与测试classpath有效,运行时无效。如在web开发时,只有在编译和测试时才用到servlet-api,将其设置为此范围,在运行时servlet-api由web容器提供,无须依赖。并且在打war包时,此范围的依赖不会打在WEB-INF/lib下。
Runtime:运行时依赖范围。与provided相对,运行时classpath有效。典型例子如jdbc(编写是接口规范运行是提供具体实现类需要jar包)。

Tomcat中自带servlet-api,运行时无效即可。
5、依赖传递及其控制
1.依赖传递
依赖传递可以理解为A依赖了B,B依赖了C,则C间接依赖了A
这里HelloFriend依赖了Hello,在MakeFriend依赖于HelloFriend时,Hello也被同时引进

2.依赖范围对依赖传递的影响
传递依赖是会受到依赖范围的影响的

3.依赖阻断
(1)暴力阻断
在目标层切断下层对上层的依赖

(2)可选依赖
需要在依赖中明确删掉某一依赖,可以使用exclusion属性,排除掉引用的依赖
HelloFriend中的依赖

MakeFriend中不需要Hello依赖和jackson-databind依赖,则可以

6、仓库
1.仓库的概念
在 Maven 的术语中,仓库是一个位置(place)。
Maven 仓库是项目中依赖的第三方库,这个库所在的位置叫做仓库。
在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。
Maven 仓库能帮助我们管理构件(主要是JAR),它就是放置所有JAR文件(WAR,ZIP,POM等等)的地方。
仓库的类型有:
- 本地(local)
- 中央(central)
- 远程(remote)
(1)本地仓库
Maven 的本地仓库,在安装 Maven 后并不会创建,它是在第一次执行 maven 命令的时候才被创建。
运行 Maven 的时候,Maven 所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件。
默认情况下,不管Linux还是 Windows,每个用户在自己的用户目录下都有一个路径名为.m2/respository/ 的仓库目录
Maven 本地仓库默认被创建在 %USER_HOME% 目录下。要修改默认位置,在 %Maven_HOME%\conf 目录中的 Maven 的 settings.xml 文件中定义另一个路径。

(2)中央仓库
Maven 中央仓库是由 Maven 社区提供的仓库,其中包含了大量常用的库。
中央仓库包含了绝大多数流行的开源Java构件,以及源码、作者信息、SCM、信息、许可证信息等。一般来说,简单的Java项目依赖的构件都可以在这里下载到。
中央仓库的关键概念:
- 这个仓库由 Maven 社区管理。
- 不需要配置。
- 需要通过网络才能访问
(3)依赖搜索顺序

私服可以自动连接中央仓库
7、生命周期
1.生命周期的概念
Maven的生命周期是对所有的构建过程进行抽象和统一。Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,生命周期只是定义了一系列的阶段,并确定这些阶段的执行顺序。而在执行这些阶段时,实际的工作还是由插件来完成的。这种思想与设计模式中的模板方法非常相似。
Maven有三套相互独立的生命周期:
Clean: clean生命周期的目的是清理项目
Default:default生命周期的目的是构建项目
site:site生命周期的目的是建立项目站点。
|--------------------|------------|-------------------------|-------------|
| 生命周期 | clean | default | site |
| 阶段(phase),执行顺序由上至下 | pre-clean | validate | pre-site |
| 阶段(phase),执行顺序由上至下 | clean | initialize | site |
| 阶段(phase),执行顺序由上至下 | post-clean | generate-sources | post-site |
| 阶段(phase),执行顺序由上至下 | | process-sources | site-deploy |
| 阶段(phase),执行顺序由上至下 | | generate-resources | |
| 阶段(phase),执行顺序由上至下 | | process-resources | |
| 阶段(phase),执行顺序由上至下 | | compile | |
| 阶段(phase),执行顺序由上至下 | | process-classes | |
| 阶段(phase),执行顺序由上至下 | | generate-test-sources | |
| 阶段(phase),执行顺序由上至下 | | process-test-sources | |
| 阶段(phase),执行顺序由上至下 | | generate-test-resources | |
| 阶段(phase),执行顺序由上至下 | | process-test-resources | |
| 阶段(phase),执行顺序由上至下 | | test-compile | |
| 阶段(phase),执行顺序由上至下 | | process-test-classes | |
| 阶段(phase),执行顺序由上至下 | | test | |
| 阶段(phase),执行顺序由上至下 | | prepare-package | |
| 阶段(phase),执行顺序由上至下 | | package | |
| 阶段(phase),执行顺序由上至下 | | pre-integration-test | |
| 阶段(phase),执行顺序由上至下 | | integration-test | |
| 阶段(phase),执行顺序由上至下 | | post-integration-test | |
| 阶段(phase),执行顺序由上至下 | | verify | |
| 阶段(phase),执行顺序由上至下 | | install | |
| 阶段(phase),执行顺序由上至下 | | deploy | |
用户在mvn命令后可以指定三个生命周期中的任何阶段,则Maven会按以下逻辑执行:首先会得到该阶段所属生命周期,从该生命周期中的第一个阶段开始按顺序执行,直至该阶段本身。例如执行mvn clean命令会依次执行clean生命周期中的pre-clean阶段及clean阶段。mvn命令后可以指定多个阶段,Maven会按照输入的顺序依次执行,每次执行都会按照之前描述的逻辑执行。
之前提到实际的工作还是由插件来完成的,这意味着插件需要和阶段绑定起来。Maven已经事先将很多核心插件绑定到对应的阶段,这样用户几乎不用配置就有构建Maven项目。Maven的内置绑定如下:
|---------|-------------------------|-------------------------------------|
| 生命周期 | 阶段(phase) | 插件目标 |
| clean | clean | maven-clean-plugin:clean |
| default | process-resources | maven-resources-plugin:resources |
| default | compile | maven-compiler-plugin:compile |
| default | generate-test-resources | maven-resources-plugin:testResouces |
| default | test-compile | maven-compiler-plugin:testCompile |
| default | test | maven-surefire-plugin:test |
| default | package | 打包类型是jar时:maven-jar-plugin:jar; |
| default | package | 打包类型是war时:maven-war-plugin:war |
| default | install | maven-install-plugin:install |
| default | deploy | maven-deploy-plugin:deploy |
| site | site | maven-site-plugin:site |
| site | site-deploy | maven-site-plugin:deploy |
8、Maven继承和聚合
1.继承的意义
继承就是避免重复,maven的继承也是这样,它还有一个好处就是让项目更加安全。比如我们在项目开发的过程中,可能多个模块独立开发,但是多个模块可能依赖相同的公共模块,比如说每个模块都需要javaseo-utils,在编译的时候,maven-compiler-plugin插件也要被引入,maven仓库地址以及发布目录都是相同的配置。我们可以使用Maven的继承功能,把公共的配置信息写到父模块中,子模块只要继承了该父模块,也会继承父模块的配置信息。
2.可继承的pom元素
groupId :项目组 ID ,项目坐标的核心元素;
version :项目版本,项目坐标的核心元素;
properties :自定义的 Maven 属性,统一管理版本;
dependencies :项目的依赖配置;
dependencyManagement :醒目的依赖管理配置,只做依赖管理,子工程使用,即在父工程中声明,在子工程中具体使用;
description :项目的描述信息;
organization :项目的组织信息;
inceptionYear :项目的创始年份;
url :项目的 url 地址
develoers :项目的开发者信息;
contributors :项目的贡献者信息;
distributionManagerment :项目的部署信息;
issueManagement :缺陷跟踪系统信息;
ciManagement :项目的持续继承信息;
scm :项目的版本控制信息;
mailingListserv :项目的邮件列表信息;
repositories :项目的仓库配置;
build :包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等;
reporting :包括项目的报告输出目录配置、报告插件配置等。
3.IDEA实现Maven的继承
创建父类项目,普通的Maven项目,主要用于管理,不涉及业务逻辑实现。

设置好groupId和artifactId,在该父类项目上创建的子项目该两项内容不可变


父项目只起到管理作用,无需设置成jar或war,设置成表示管理的pom,src目录也可以直接删掉

创建普通MavenJava子项目core。右键点击父项目

groupId和ArtifactId不能被更改了

创建MavenJavaWeb项目console

4.继承的依赖管理
(1)父类直接引用依赖
如果在父类的dependencies内直接引用依赖,则子类都会继承该依赖

父类中依赖了jackson-databind.2.19.0,子类中均依赖该资源。
如果刷新了Maven依赖还没有显示的话,可以关闭项目,重启IDEA,重新打开项目。
(2)父类使用dependencyManagement管理
子类不会默认继承该依赖,但是当子类使用该依赖时无需考虑版本信息,直接继承父类dependencyManagement中设置的版本号(Version)
5.聚合管理
我们在平时的开发中,项目往往会被划分为好几个模块,比如common公共模块、system系统模块、log日志模块、reports统计模块、monitor监控模块等等。这时我们肯定会出现这么一个需要,我们需要一次构件多个模块,而不用每个模块都去mvn; 以上面得父子项目为例,当我们对parent进行mvn install时,会对core,manage和portal项目均进行install操作
6.properties属性的使用
通过 properties元素用户可以定义一个或多个 maven 属性,然后在 maven 的其他地方使用 ${属性名称} 的方式引用该属性,这种做法的意义在于消除重复和统一管理。比如,需要在多个地方重复声明同样的 SpringFramework 版本,现在只需要在一个地方声明就可以。