Maven 的配置和依赖是单根继承的 
Maven 的模块继承是无法进行多继承的,只能使用单根继承。
Maven 中 dependencies 与 dependencyManagement 的区别 
dependencies 即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)
dependencyManagement 里只是声明依赖和它们的版本,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且 version 和 scope 都读取自父 pom; 另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
有效 pom 
1 2 3 mvn help :effective-pom > effective-pom.xml
下载依赖中的源码和文档 
在 maven 里,resolve 有特别的含义:仲裁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 true 
在 .m2 中这样配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <settings > <profiles > <profile > <id > downloadSources</id > <properties > <downloadSources > true</downloadSources > <downloadJavadocs > true</downloadJavadocs > </properties > </profile > </profiles > <activeProfiles > <activeProfile > downloadSources</activeProfile > </activeProfiles > </settings > 
在项目中这样主动配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-dependency-plugin</artifactId > <version > 3.1.2</version > <executions > <execution > <goals > <goal > sources</goal > <goal > resolve</goal > </goals > <configuration > <classifier > javadoc</classifier > </configuration > </execution > </executions > </plugin > </plugins > </build > 
附赠一个脚本:
1 2 vi complete-build.shchmod  a+x complete-build.sh
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 32 33 34 35 36 #!/bin/bash function  traverseAllProjects for  f in  *; do if  [ -d "$f "  ]; then echo  "coming into a folder $f " cd  $f echo  "current path:" pwd if  test  -f "$POM_FILE " ; then echo  "$POM_FILE  exists." time  mvn clean install -Dmaven.test.skip=true time  mvn dependency:sources dependency:resolve -Dclassifier=javadoc\ntime  mvn clean install -Dmaven.test.skip=true fi if  test  -f "$GRADLE_FILE " ; then echo  "$GRADLE_FILE  exists." time  ./gradlew clean build -x test fi echo  "coming out a folder $f " cd  ..echo  "current path:" pwd fi done time  traverseAllProjects
在 idea中这样配置 Preference > Build, Execution, Deployment > Build Tools > Maven > importing。
bom 和 pom 
BOM 
BOM定义 
BOM(Bill of Material),物料清单。将某一个领域相应的jar统一称之为XXX-BOM予以管理(例如inf-bom、spring-framework-bom),并不是Maven的规定,只是一种约定俗成(convention over configuration)。Gradle 也采用了这样的思路 。
为什么要使用bom 
(1)避免开发同学需要关心各个API的版本关系:BOM这种方式在 dependencyManagement 指定了各个依赖的版本,并不会使得这些依赖并被真正引入(仅做版本管理使用)。在dependency中还要按需引入、但省去了需要制定version的麻烦。
(2)便于历史版本API的收归。
(3)可以说逐渐使用 bom 来统一管理某个团队(领域)的 API 版本已成为一种趋势。
scope 的含义 
scope元素的作用:控制 dependency 元素的使用范围。通俗的讲,就是控制 Jar 包在哪些范围被加载和使用。
compile(默认) 
含义:compile 是默认值,如果没有指定 scope 值,该元素的默认值为 compile。被依赖项目需要参与到当前项目的编译,测试,打包,运行等阶段。打包的时候通常会包含被依赖项目。
provided 
含义:被依赖项目理论上可以参与编译、测试、运行等阶段,相当于compile,但是再打包阶段做了exclude的动作。
runtime 
含义:表示被依赖项目无需参与项目的编译,但是会参与到项目的测试和运行。与compile相比,被依赖项目无需参与项目的编译。
test 
含义: 表示被依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。
system 
含义:system 元素与 provided 元素类似,但是被依赖项不会从 maven 仓库中查找,而是从本地系统中获取,systemPath 元素用于制定本地系统中 jar 文件的路径。例如:
1 2 3 4 5 6 7 <dependency > <groupId > org.open</groupId > <artifactId > open-core</artifactId > <version > 1.5</version > <scope > system</scope > <systemPath > ${basedir}/WebContent/WEB-INF/lib/open-core.jar</systemPath > </dependency > 
import 
它只使用在中,表示从其它的pom中导入dependency的配置,例如 (B项目导入A项目中的包配置): 
想必大家在做SpringBoot应用的时候,都会有如下代码:
1 2 3 4 5 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 1.3.3.RELEASE</version > </parent > 
继承一个父模块,然后再引入相应的依赖。
我们知道Maven的继承和Java的继承一样,是无法实现多重继承的,如果10个、20个甚至更多模块继承自同一个模块,那么按照我们之前的做法,这个父模块的dependencyManagement会包含大量的依赖。如果你想把这些依赖分类以更清晰的管理,那就不可能了,import scope依赖能解决这个问题。你可以把 dependencyManagement 放到单独的专门用来管理依赖的pom中,然后在需要使用依赖的模块中通过import scope依赖,就可以引入 dependencyManagement。例如可以写这样一个用于依赖管理的pom:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <project > <modelVersion > 4.0.0</modelVersion > <groupId > com.test.sample</groupId > <artifactId > base-parent1</artifactId > <packaging > pom</packaging > <version > 1.0.0-SNAPSHOT</version > <dependencyManagement > <dependencies > <dependency > <groupId > junit</groupId > <artifactid > junit</artifactId > <version > 4.8.2</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactid > log4j</artifactId > <version > 1.2.16</version > </dependency > </dependencies > </dependencyManagement > </project > 
然后我就可以通过非继承的方式来引入这段依赖管理配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependencyManagement > <dependencies > <dependency > <groupId > com.test.sample</groupId > <artifactid > base-parent1</artifactId > <version > 1.0.0-SNAPSHOT</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependency > <groupId > junit</groupId > <artifactid > junit</artifactId > </dependency > <dependency > <groupId > log4j</groupId > <artifactid > log4j</artifactId > </dependency > 
注意:import scope只能用在 dependencyManagement 里面。这么多的 scope 里面,import 也因此是最危险的,因为 import 会把依赖直接展开,而不是用间接传递的方式在新应用中体现,会覆盖 parent 和 dependency(因为寻根路径最短,链接器会最先被链接上),而且无法被 exclude 排除 。 
这样,父模块的pom就会非常干净,由专门的packaging为pom来管理依赖,也契合的面向对象设计中的单一职责原则。此外,我们还能够创建多个这样的依赖管理pom,以更细化的方式管理依赖。这种做法与面向对象设计中使用组合而非继承也有点相似的味道。
那么,如何用这个方法来解决SpringBoot的那个继承问题呢?
配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > 1.3.3.RELEASE</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies > 
这样配置的话,自己的项目里面就不需要继承SpringBoot的module了,而可以继承自己项目的module了。
scope的依赖传递 
A–>B–>C。当前项目为A,A依赖于B,B依赖于C。知道B在A项目中的scope,那么怎么知道C在A中的scope呢?答案是:
依赖矩阵表格见:《Introduction to the Dependency Mechanism》 
scope 与 optional 的区别 
maven的 scope 决定依赖的包是否加入本工程的classpath下。- 某些 scope 连本项目的 classpath 都会被影响。使用本项目的项目无论如何都不能绕过 scope 的影响,scope 才是最彻底的对传播的隔离(比如 provided)。
optional仅限制依赖包的传递性,不影响依赖包的classpath。- 不影响本项目生成的 jar,影响使用本项目的项目。
scope 与 optional 都可以用重新声明依赖的方式来引入缺失依赖。
比如一个工程中
A->B, B->C(scope:compile, optional:true),B的编译/运行/测试classpath都有C,A中的编译/运行/测试classpath都不存在C(尽管C的scope声明为compile),A调用B哪些依赖C的方法就会出错。
A->B, B->C(scope:provided), B的编译/测试classpath有C,A中的编译/运行/测试classpath都不存在C,但是A使用B(需要依赖C)的接口时就会出现找不到C的错误,此时,要么是手动加入C的依赖,即A->C,否则需要容器提供C的依赖包到运行时classpath。
对于纯粹作为 lib 来用的 jar,rovided over optional。因为出了 test 这个 phase,连 jar 都不能独立 run 起来。optional 本身是一个可以自己在各种 phase run,但被依赖的时候则会去除打包配置,依然会影响 classpath。
debug 小技巧 
在子工程里显式地指定某个依赖版本看是否能够消除错误。 
使用 ide 的依赖分析工具,如 mvn dependency 插件(这个分析工具只是运行时分析,有误导性)或者 idea 的 dependency analyzer。 
显式地消除依赖: 
 
1 2 3 4 5 <exclusions > <exclusion > <groupId > org.springframework</groupId >                               <artifactId > spring-context-support</artifactId > </exclusion > </exclusions > 
《Optional Dependencies and Dependency Exclusions》 :
Optional dependencies are used when it’s not possible (for whatever
However, since the project cannot be split up (again, for whatever
 
optional 是大项目无法被切割成小的子模块的无奈选择,如果项目要使用被依赖模块的可选功能,必须显式地再声明一遍可选依赖,否则会产生调用出错。optional 阻断了传递依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 <project > <dependencies > <dependency > <groupId > sample.ProjectA</groupId > <artifactId > Project-A</artifactId > <version > 1.0</version > <scope > compile</scope > <optional > true</optional >  </dependency > </dependencies > </project > 
Project-A -> Project-B Project-X -> Project-A
A 的类路径里有 B,而 X 的类路径里无 B。
怎样使用 maven 的 version 插件? 
参考:《Use the Latest Version of a Dependency in Maven》 
Maven2 also provided two special metaversion values to achieve the
 
但这两个值已经过期了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <build > <plugins > <plugin > <groupId > org.codehaus.mojo</groupId > <artifactId > versions-maven-plugin</artifactId > <version > 2.7</version > <configuration > <excludes > <exclude > org.apache.commons:commons-collections4</exclude > </excludes > </configuration > </plugin > </plugins > </build > 
1 2 3 4 5 6 7 8 9 10 
1 2 3 4 5 6 7 8 9 <plugin > <groupId > org.codehaus.mojo</groupId > <artifactId > versions-maven-plugin</artifactId > <version > 2.7</version > <configuration > <rulesUri > http://www.mycompany.com/maven-version-rules.xml</rulesUri > </configuration > </plugin > 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <ruleset  comparisonMethod ="maven"    xmlns ="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0"    xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation ="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0     http://mojo.codehaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd" ><ignoreVersions > <ignoreVersion  type ="regex" > .*-beta</ignoreVersion > </ignoreVersions > </ruleset > <ruleset  comparisonMethod ="maven"    xmlns ="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0"    xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation ="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0       http://mojo.codehaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd" ><rules > <rule  groupId ="com.mycompany.maven"  comparisonMethod ="maven" > <ignoreVersions > <ignoreVersion  type ="regex" > .*-RELEASE</ignoreVersion > <ignoreVersion > 2.1.0</ignoreVersion > </ignoreVersions > </rule > </rules > </ruleset > 
参考:
《Maven依赖中scope的含义》 《maven scope-provided 与 optional 区别》