一、Maven多模块的意义
当我们的项目模块很多的时候,我们使用Maven管理项目非常方便,帮助我们管理构建、文档、报告、依赖、scms、发布、分发的方法。可以方便的编译代码、进行依赖管理、管理二进制库等等。
由于我们的模块很多,所以我们又抽象了一层,抽出一个父项目来管理子项目的公共的依赖。为了项目的正确运行,必须让所有的子项目使用依赖项的统一版本,必须确保应用的各个项目的依赖项和版本一致,才能保证测试的和发布的是相同的结果,父项目就是为了保证这一点的。
在我们项目顶层的POM文件中,我们会看到dependencyManagement元素。通过它元素来管理jar包的版本,让子项目中引用一个依赖而不用显示的列出版本号。Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号。(只有父项目中才有dependencyManagement标签,子项目都不会有的,这个标签默认出现在Spring-boot-starter中)

二、Spring多模块的POM设置
1.父项目
父项目用dependencyManagement标签来管理依赖什么jar包,用properties标签来设置依赖的jar包的版本。

2.子项目
子项目不再需要指定依赖的版本了,只需要写dependency依赖什么jar包就可以了,不需要指定版本。
子项目必须用parent指定父项目。
所有子项目共享依赖的版本,也就是用父项目的POM来统一管理所有子项目的依赖的版本。

三、Maven依赖的规则
1.依赖范围
Scope标签用来指定依赖范围。
(1)compile: 编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的maven依赖,对于编译 测试 运行三种的classpath都有效。
(2)test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试的classpath有效,在编译主代码或者运行主代码的时候都无法依赖此类依赖。典型的例子是jUnit,它只有在编译测试代码及运行测试代码的时候才有效。
(3)provided:以提供依赖范围。使用此依赖范围的maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行的时候,由于容器已经提供,就不需要maven重复地引入一遍。打包的时候可以不用包进去,别的设施会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是打包阶段做了exclude操作
(4)runtime:运行时依赖范围。使用此依赖范围的maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要jdk提供的jdbc的接口,只有在执行测试或者运行测试的时候才需要实现上述接口的jdbc的驱动
(5)system:系统依赖范围。从参与度来说,和provided相同,不过被依赖项不会从maven仓库下载,而是从本地文件系统拿。需要添加systemPath的属性来定义路径,该依赖与三种范围的classpath和provided依赖范围完全一致。可能造成不可移植,谨慎使用。
(6)import:导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。只有在dependencyManagement下才有效果。

2.依赖传递
A依赖B,B依赖C。当前项目为A,A是否依赖C呢?
(1)当C是test或者provided时,C直接被丢弃,A不依赖C;(排除传递依赖)
(2)其他情况A依赖C,A依赖C的scope和B依赖C的scope是一样的。

3.依赖顺序
Maven的依赖是按照依赖树的就近原则。Maven的依赖会有依赖树,在依赖树中判定依赖的类型。通过mvn dependency:tree可以看依赖树。

结论:
(1)由于就近原则,所以子POM文件肯定能覆盖父POM文件的依赖设置。
(2)如果两个依赖的路径长度是一样的,那么使用首先声明原则。maven会根据pom文件声明的顺序加载,如果先声明了B,后声明了C,那就最后的依赖就会是B。