什么是maven?
乍一看,maven好像包含的东西很多。简单说来,maven试图为创建的项目提供一个结构模型,用最佳的实践方式,定义一个清晰的项目结构,以促进理解和效率。从本质上讲,maven是一个项目管理的通用性工具,其可为项目的创建、文档、报告、依赖、发布等提供管理支持。
Maven有一套标准的规范和实践,可以缩短开发周期,并且减小出错的概率。
Maven项目的结构
一个基本的maven项目结构如下:
my-app
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- com
| | `-- mycompany
| | `-- app
| | `-- App.java
| `-- resources
| `-- META-INF
| `-- application.properties
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
上图所示为maven项目的标准结构:${basedir}/src/main/java/是项目的程序文件;${basedir}/src/test/java是项目测试用的程序文件。${basedir}代表pom.xml文件所在路径。
项目中通常会有很多的配置文件,如log4j、databaseConfig、properties等,可以定义在${basedir}/src/main/resources/路径下。
上图结构执行package命令后,打包的jar文件结构路径如下:
|-- META-INF
| |-- MANIFEST.MF
| |-- application.properties
| `-- maven
| `-- com.mycompany.app
| `-- my-app
| |-- pom.properties
| `-- pom.xml
`-- com
`-- mycompany
`-- app
`-- App.class
由此可见maven项目结构的重要性,因为jar包的结构是根据开发定义的路径来生成的。所以一个好的结构,生成的jar文件结构更易理解。
Maven为了满足项目需要,还可以有更多的结构定义,具体参考:
http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
自定义本地仓库位置
文件${maven_home}/conf/settings.xml,是一个maven的配置文件,可以设置很多参数值。其中有一个标签<localRepository>用于设置本地仓库的位置。一开始,此标签是被注释掉的,因为默认情况,本地仓库的位置在${user.home}/.m2/repository路径下。我们只需要将此标签拷贝到注释之外,并且定义标签值(标签值要定义为绝对路径),就修改了本地仓库的位置。如:
<localRepository>D:/maven_home/repository</localRepository>
Settings.xml文件还可以设置更多的参数,可以参考以下链接:
http://maven.apache.org/ref/2.2.1/maven-settings/settings.html
Pom.xml文件
Maven概念基于项目对象模型(project object model,POM),maven项目的pom.xml则配置了项目对象的信息。POM是Maven的基本单元,因为maven以项目为核心,一个项目就是一个对象,一切的解决方案都是围绕项目来工作的。所以理解POM的概念和配置。下面是一些简单的POM配置:
1) Project:pom.xml的根节点。
2) modelVersion:POM版本,很少变化,不用修改。
3) groupId:此元素是一个组织创建的一个项目的唯一标示,groupId是一个项目的唯一标示,通常的命名规范是:部门网址后半部分反写+项目名。如groupId:org.apache.maven.plugins就是为maven提供插件支持的一个项目。org.apache.maven是maven.apache.org的反写+项目名plugins。
4) artifactId:此元素是项目的模块名,多个模块组成一个项目。项目最基本的模块是jar文件,maven打包一个模块生成的jar文件命名规范为:<artifactId>-<version>.<extension>(例如:my-app-1.0.jar)。这里可看出,maven中创建的项目是以模块为单位,因为最终生成的是一个jar包。
5) packaging:此元素定义了最终打包生成文件的格式,可以是jar、war、ear等。默认是生产jar文件。
6) version:此元素定义了当前模块的版本,SNAPSHOT通常表示此版本是一个开发的状态。
7) name:项目名称,用于生产文档。
8) url:项目站点,用于生产文档。
9) description:项目描述,用于生成文档。
更多参考:http://maven.apache.org/ref/3.2.5/maven-model/maven.html
更多POM标签,参考:
http://maven.apache.org/ref/3.2.5/maven-model/maven.html#class_DependencyManagement
POM概念
POM是maven基础结构单元,它是一个包含项目信息和配置的XML文件,并且POM为大部分项目提供了很多默认值。如target代表项目构件路径,src/main/java/代表源路径,src/test/java代表测试路径等。
POM在maven1.0中用project.xml来定义,在maven2.0后用pom.xml来定义。Maven1.0定义目标或插件用到配置文件maven.xml,但在2.0后,此配置文件已经融合到pom.xml中。
POM中可以定义的内容包括:项目依赖(jar)、插件或目标、profiles等等。还可以定义一些其他信息,如:项目版本、项目描述、开发者信息、邮件列表等等。
Super POM
Super POM是maven的默认POM(这是一个继承的概念,就像java中父类的关键字super含义一样,代表父一级的POM)。所有的POM都继承Super POM——就好像所有java类都继承Object一样,这意味着自定义的POM将继承Super POM中定义的属性值。
那么Super POM都包含哪些属性值呢?比如,远程仓库位置、项目结构路局等。如果我们的项目需要改变这些属性的话,就需要在项目中明确的定义这些值,以覆盖super POM中的值。
具体Super POM中定义了那些值,可以参考片段:
%M2_HOME%/lib/ maven-model-builder-3.2.5.jar/org/apache/maven/model/pom4.0.0.xml
最小的POM
一个POM至少需要以下的配置:
1) project root
2) modelVersion——一般设置为4.0.0
3) groupId——项目组的id
4) artifactId——项目(模块)的id
5) version——项目(模块)的版本
示例如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
一个POM必须配置groupId、artifactId、version,这三个属性值确定了一个项目(模块)的全限定名。我们描述时,可以用<groupId>:<artifactId>:<version>来代表一个项目(模块)。如上面例子的全限定名为:com.mycompany.app:my-app:1。
如前所述,如果我们的POM中的一些细节未定义,那么这些细节配置将继承与super POM。如<packaging>标签,若不指定,就是默认值jar。
同样上面的例子也未定义仓库标签<repositories>,那么他会默认继承super POM的仓库标签值:http://repo.maven.apache.org/maven2。这样我们的maven项目将知道从哪里下载依赖。
POM继承
Super POM就是一个继承的例子。此外,我们也可以自定义父一级的POM,供其他POM继承。
什么时候需要定义父一级的POM?
当我们开发多个关联项目,都用到一些共同的配置,为了保持项目的一致性,且代码编写量最少,那就可以定义一个父POM来配置这些公共特性。
如何定义一个父POM?
定义一个父POM与定义一个普通POM一模一样,把公共部分挑出来放入一个POM即可;继承父POM的子POM有一点区别:需要在子POM定义<parent>标签包含的信息,这些信息就是父POM的全限定名(groupId、artifactId、version)。
示例1
假设现在有这么两个POM:<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>
这两个POM的路径结构如下:
.
|-- my-module
| `-- pom.xml
`-- pom.xml
即my-app的POM是父一级,而my-module是子一级POM。
现在,若我们想要让my-module继承my-app POM,我们需要修改my-module的POM,增加配置<parent>标签信息。Com.mycompany.app:my-module:1的POM如下:
<project>
<span style="color:#ff0000;"><parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent></span>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>
有了上面的配置,my-module的POM就可以继承my-app的POM中的属性值了。
有一点需要注意一下,如果我们想让子POM中的groupId或者version属性与父POM的值保持一致,那么可以删除子POM中这两项的定义,直接从父POM中继承。修改如下:
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-module</artifactId>
</project>
示例2
示例1中子项目是在父项目的子目录中。如果现在两个项目在同一级目录中,但二者是继承关系,又该如何处理?项目结构如下:
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml
为了解决这个问题,在子POM的<parent>标签定义中,还需添加定义一个子标签<relativePath>。顾名思义,其定义了父POM的相对路径。示例如下:
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<span style="color:#ff0000;"><relativePath>../parent/pom.xml</relativePath></span>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-module</artifactId>
</project>
POM组合
项目的组合与项目的继承很像。但与继承不同:继承是在子POM中定义父POM的信息,而组合是在父一级的POM定义被组合对象的POM信息。这就需要我们提前知道我们需要组合哪些模块,这样当我们对父一级的POM进行编译,那么其组合模块也能一起编译。
什么时候需要使用POM组合?
一个项目由若干个模块组成,每次编译都需要手动对每个模块一一编译,这样就很麻烦。因此,我们定义一个父一级的POM,将这些模块组合起来,编译一个POM,组合模块一一起被编译。
如何定义一个组合POM?
定义一个组合POM项目,需要注意两点:
1)将父POM中<packaging>属性值修改为pom
2)在父POM中,通过<module>标签明确定义要组合的子POM。
示例
这里还是引用POM继承中示例一的两个POM为例。
若两个POM结构为:.
|-- my-module
| `-- pom.xml
`-- pom.xml
我们组合的定义,应该修改com.mycompany.app:my-app:1的POM如下:
<project>若两个POM结构为:
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<span style="color:#ff0000;"><packaging>pom</packaging></span>
<span style="color:#ff0000;"><modules>
<module>my-module</module>
</modules></span>
</project>
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml我们组合的定义,应该修改com.mycompany.app:my-app:1的POM如下:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1</version> <packaging>pom</packaging> <modules> <span style="color:#ff0000;"><module>../my-module</module></span> </modules></project><module>值
由上面的示例可以看出,<module>的值与被组合POM的artifactId和相对路径有关。可以简单描述为:相对路径 + artifactId。
POM的继承和组合
POM的继承和组合可以同时使用。就是说,你可以在父POM中定义module的值,还需要在module的POM定义<parent>的信息。这样的配置需要注意3点:
1)在每个子POM中定义<parent>信息
2)将父POM的<packaging>值修改为pom
3)在父POM中定义<module>的信息
示例
还是POM继承中的两个例子。若其结构为:
. |-- my-module | `-- pom.xml `-- parent `-- pom.xml则com.mycompany.app:my-app:1的POM配置为:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1</version> <span style="color:#ff0000;"><packaging>pom</packaging> <modules> <module>../my-module</module> </modules></span></project>com.mycompany.app:my-module:1的POM配置为:
<project> <span style="color:#ff0000;"><parent> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1</version> <relativePath>../parent/pom.xml</relativePath> </parent></span> <modelVersion>4.0.0</modelVersion> <artifactId>my-module</artifactId></project>注意:maven中还有一个profile的配置,其继承的策略与POM的继承策略一样。
POM变量和占位符
在实际应用中,maven不推荐重复配置。然而,在一些情景中,同一个值可能需要配置在多个不同的地方。为了能让一个值只定义一次,maven提供两种解决方案:POM的预定义变量、定义<properties>属性。
POM预定义变量
POM中预定的变量有两种:一种是POM中所有的标签值,都可以作为一个元素值被引用;另一种是特殊的变量。
POM标签值引用,前缀project代表POM本身。如:
${project.groupId},
${project.version}
${project.build.sourceDirectory}
${project.version}(这里的version必须在父POM中定义过)
特殊的变量包括:
project.basedir:项目路径
project.baseUri项目uri
maven.build.timestamp:项目创建开始时间。可以被格式化,参考原文。
<properties>定义
为了能引用变量,可以在POM中用<properties>标签定义变量。如:<project> ... <span style="color:#ff0000;"><properties> <mavenVersion>2.1</mavenVersion> </properties></span> <dependencies> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-artifact</artifactId> <span style="color:#ff0000;"><version>${mavenVersion}</version></span> </dependency> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-project</artifactId> <span style="color:#ff0000;"><version>${mavenVersion}</version></span> </dependency> </dependencies> ...</project>Maven的生命周期
maven的阶段有很多,下面是常用的几个:
1)validate:验证项目,以及项目所有信息的正确性
2)compile:编译源代码
3)test:用合适的单元测试框架测试编译后的代码,测试用的代码不需要被打包和发布。
4)package:将编译后的代码打包成可发布的格式(如jar包)
5)integration-test:如果需要的话,将package部署到一个集成环境中,以供测试
6)verify:运行检查,以验证package的可用性,以及包的质量性能
7)install:将我们的package发布到本地仓库中,这样我们的项目模块就可以被其他模块调用。
8)deploy:在集成或部署环境中完成,将我们最终的package拷贝到远程仓库中,以便其他开发者和模块的调用。
还有两个描述maven声明周期的术语,如下:
9)clean:清理删除先前创建的artifacts
10)site:为项目生产站点文件
参考地址: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
Maven的依赖特性
依赖管理是maven的一大优势。如果我们只有一个单独的项目,管理依赖并不难。但若我们的项目或应用由成百上千个模块构成,那么maven将为项目提供高的可控性和稳定性。
依赖传递
依赖传递是maven2.0的特征。依赖传递是指:我们在项目A中定义依赖B,可能B还依赖C,那么A中无需再定义依赖C,其已自动包含在A中。也就是依赖能传递的意思。
能被传递的依赖,可以是依赖的依赖,可以是依赖的继承。可传递的依赖数量没有限制,但可能会由于依赖关系造成依赖冲突。
为了解决依赖冲突,依赖类库增长迅速的情况,有一些额外的控制手段。如下:
1) 依赖调停:依赖调停发生在同一个类库的不同版本,直接或间接的都依赖于本项目,其需要决定使用此类库的哪一个版本。Maven提供了一种解决方案,我将之称为“亲戚关系”。即谁的亲戚关系越近,优先使用谁的版本;如果亲戚关系同级,那么谁先定义使用谁。
举例:
若有这样的依赖关系:A->B->C->D2.0和A->E->D1.0,很明显A中将依赖D1.0的版本;
若有这样的依赖关系:A->B->C->D2.0和A->F->E->D1.0,那么A中依赖D的版本,取决于A的POM中,B和F的定义顺序。
2) 依赖管理:若有依赖传递的情况,maven允许直接指定依赖版本。依赖调停中依赖D直接传递给了A,即便A并未直接引用它;相反,依赖管理可以直接在A的<dependencyMangement>标签中定义D的依赖,直接控制D的版本。
3) 依赖范围:依赖范围可以控制依赖在maven生命周期的各个阶段中是否可用。
4) 排斥依赖:若项目X->Y->Z,那项目X中可以用<exclusion>标签指定排除Z依赖。
5) 可选依赖:若项目Y->Z,那项目Y中可用<optional>标签将Z标记为可选依赖。当有项目X->Y,X只有Y的依赖,但没有Z依赖。此时我们需要在X中定义自己的Z依赖。
依赖范围
依赖范围用标签<scope>控制,有6个可选值:
1) Compile:范围指的是编译范围有效,在编译和打包时都会将依赖存储进去
2) Test:范围指的是测试范围有效,在编译和打包时都不会使用这个依赖
3) Provided:在编译和测试的过程有效,最后打包的时候不会加入,诸如servle-api,因为servlet-api在tomcat等web服务器中已经存在,如果打包会有冲突。
4) Runtime:在运行的时候依赖,在编译的时候不依赖
5) System:告诉maven,此依赖来自JDK或VM,不会从仓库中获取依赖。
6) Import:此属性值只可以用在<dependencyManagement>标签块中,代表导入一个依赖集合。在POM中定义过的依赖,将被<dependencyManagement>中定义的同名依赖替换,因此<dependencyManagement>标签中的依赖也不受依赖传递“亲戚关系”的限制。
依赖管理
<dependencyManagement>标签用于集中管理依赖信息。
<dependencyManagement>与<depemdencies>标签的区别
<dependencyManagement>标签管理的是间接依赖,即依赖的依赖,如:A->B->C,那此标签中管理的是C一级的依赖。
不被<dependencyManagement>包含的<dependencies>,管理的是直接依赖。如:A->B->C,此标签中管理的是B一级的依赖。
换句话说,如果在父项目中定义多个子项目的公共依赖,这些依赖应该被包含在<dependencyManagement>标签中。因为这属于本项目(父项目)的间接依赖;<dependencyManagement>标签中的依赖会替换同名间接依赖,但对同名直接依赖无效。
因此,若有同名依赖,其优先级从高到低为:直接依赖 > 依赖管理定义 > 亲戚关系 > 定义顺序。
示例1
<dependencyManagement>的一个作用是在父项目中定义几个子项目的公共依赖。几个项目继承自一个公共父项目,我们会将共同部分挑出来放到父项目中定义,然后在子项目中简单引用即可。示例如下:
项目A:<project> ... <dependencies> <dependency> <groupId>group-a</groupId> <artifactId>artifact-a</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>group-c</groupId> <artifactId>excluded-artifact</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>group-a</groupId> <artifactId>artifact-b</artifactId> <version>1.0</version> <type>bar</type> <scope>runtime</scope> </dependency> </dependencies></project>项目B:<project> ... <dependencies> <dependency> <groupId>group-c</groupId> <artifactId>artifact-b</artifactId> <version>1.0</version> <type>war</type> <scope>runtime</scope> </dependency> <dependency> <groupId>group-a</groupId> <artifactId>artifact-b</artifactId> <version>1.0</version> <type>bar</type> <scope>runtime</scope> </dependency> </dependencies></project>A、 B都包含一个公共的依赖,我们可以如此定义父项目:
<project> ... <span style="color:#ff0000;"><dependencyManagement></span> <dependencies> <dependency> <groupId>group-a</groupId> <artifactId>artifact-a</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>group-c</groupId> <artifactId>excluded-artifact</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>group-c</groupId> <artifactId>artifact-b</artifactId> <version>1.0</version> <type>war</type> <scope>runtime</scope> </dependency> <dependency> <groupId>group-a</groupId> <artifactId>artifact-b</artifactId> <version>1.0</version> <type>bar</type> <scope>runtime</scope> </dependency> </dependencies> <span style="color:#ff0000;"> </dependencyManagement></span></project>然后,项目A和B可以定义的更简单:
项目A修改:<project> ... <dependencies> <dependency> <groupId>group-a</groupId> <artifactId>artifact-a</artifactId> </dependency> <dependency> <groupId>group-a</groupId> <artifactId>artifact-b</artifactId> <!-- This is not a jar dependency, so we must specify type. --> <type>bar</type> </dependency> </dependencies></project>项目B修改:<project> ... <dependencies> <dependency> <groupId>group-c</groupId> <artifactId>artifact-b</artifactId> <!-- This is not a jar dependency, so we must specify type. --> <type>war</type> </dependency> <dependency> <groupId>group-a</groupId> <artifactId>artifact-b</artifactId> <!-- This is not a jar dependency, so we must specify type. --> <type>bar</type> </dependency> </dependencies></project>注意:这里的依赖省略了<version>和<type>。省略<version>,则值与父项目中定义保持一致;省略<type>,则值为默认的jar。
示例2
<dependencyManagement>的另一个作用是管理依赖传递的版本。依赖传递是一个间接的概念,如果传递过来的依赖版本,与<dependencyManagement>中定义的版本不一致,将以标签中的版本为准。如:
父项目A:<project> <modelVersion>4.0.0</modelVersion> <groupId>maven</groupId> <artifactId>A</artifactId> <packaging>pom</packaging> <name>A</name> <version>1.0</version> <dependencyManagement> <dependencies> <dependency> <groupId>test</groupId> <artifactId>a</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>test</groupId> <artifactId>b</artifactId> <version>1.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>test</groupId> <artifactId>c</artifactId> <version>1.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>test</groupId> <artifactId>d</artifactId> <version>1.2</version> </dependency> </dependencies> </dependencyManagement></project>子项目B:<project> <parent> <artifactId>A</artifactId> <groupId>maven</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>maven</groupId> <artifactId>B</artifactId> <packaging>pom</packaging> <name>B</name> <version>1.0</version> <dependencyManagement> <dependencies> <dependency> <groupId>test</groupId> <artifactId>d</artifactId> <version>1.0</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>test</groupId> <artifactId>a</artifactId> <version>1.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>test</groupId> <artifactId>c</artifactId> <scope>runtime</scope> </dependency> </dependencies></project>如上,项目B继承了项目A。此例中,当maven运行项目B,依赖a、b、c、d的运行版本都将是1.0。说明:
1) 依赖a:在B中直接以全限定名定义,优先级最高。采用版本1.0。
2) 依赖c:在B中定义时,为指定版本。那此依赖至少应该在父项目中定义过,版本以父项目中的定义保持一致,也为1.0。
3) 依赖b:在父项目A的<dependencyManagement>中定义,这代表在a或c的POM中若有b的依赖,b1.0将会被使用。
4)依赖d:在子项目B的<dependencyManagement>中定义,这代表d有可能是a或c的依赖。在依赖传递时,无论a或c对d的依赖是什么版本,d将采用依赖管理中的定义的1.0版本。并且,d在子项目B和父项目A的依赖管理中均有定义,但子项目B的定义将覆盖父项目A中对应的定义。依赖管理小结
为简便起见,<dependencyManagement>标签简写为tag-dm;不被tag-dm包含的标签<dependencies>简写为tag-ds-ndm。
1) 若父项目中的依赖要以子项目的公共部分被定义,定义应该包含在tag-dm中。
2) 若父项目要管理子项目的依赖版本,除了父项目中要定义tag-dm,子项目还要定义对应的tag-ds-dm。
3) 若父项目中包含tag-dm定义x1.2,子项目也包含tag-dm定义x1.0,那么x的版本以子项目中的定义为准。
4) 若父项目中包含tag-dm定义x1.2,而子项目并无tag-dm定义x,那么子项目依赖传递的版本与父项目的x1.2保持一致。
5) 在tag-dm中定义的依赖,并不会完全引入项目,而是只有当依赖传递涉及到对应的已定义版本,才会被引入,且版本与定义保持一致。(待验证)
6) 项目B继承项目A,只会继承一些标签值,但不会继承tag-ds-ndm依赖。(待验证)
导入依赖
导入依赖的功能只有maven2.0.9及以后的版本才支持。在使用时,务必确认版本正确。
前面的例子以继承的方式管理依赖,但maven也只是支持单继承,对于很大很大的项目,单继承可能难以满足功能需求。为了解决此需求,maven提供了import来导入其他项目的依赖管理。
示例1
这是一个项目B:
<project> <modelVersion>4.0.0</modelVersion> <groupId>maven</groupId> <artifactId>B</artifactId> <packaging>pom</packaging> <name>B</name> <version>1.0</version> <span style="color:#ff0000;"><dependencyManagement></span> <dependencies> <dependency> <groupId>maven</groupId> <artifactId>A</artifactId> <version>1.0</version> <span style="color:#ff0000;"><type>pom</type> <scope>import</scope></span> </dependency> <dependency> <groupId>test</groupId> <artifactId>d</artifactId> <version>1.0</version> </dependency> </dependencies> <span style="color:#ff0000;"></dependencyManagement></span> <dependencies> <dependency> <groupId>test</groupId> <artifactId>a</artifactId> <version>1.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>test</groupId> <artifactId>c</artifactId> <scope>runtime</scope> </dependency> </dependencies></project>假设A是一个先前定义的例子,那么A的<dependencyManagement>中的依赖定义都会被导入B的<dependencyManagement>中。注意d例外,因为其在B中已定义。
示例2
项目X:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>X</artifactId>
<packaging>pom</packaging>
<name>X</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
项目Y:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>Y</artifactId>
<packaging>pom</packaging>
<name>Y</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
项目Z:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>Z</artifactId>
<packaging>pom</packaging>
<name>Z</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>maven</groupId>
<artifactId>X</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>maven</groupId>
<artifactId>Y</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>在Z中导入了X和Y,但X和Y都包含a定义。那么a1.1将被包含在Z中,因为a1.1的定义在前。
导入依赖也是可传递的。如若X中导入了Q,当Z中导入X,Q也同样导入到Z中。
更多示例请参考官方文档。地址:
导入依赖小结
Import值只能用在<dependencyManagement>标签中;
Import的POM,<type>必须为pom;
不能导入子项目的依赖。这是一个加载顺序的问题。
依赖控制
Maven在定义一个依赖时,提供两个标签来控制依赖是否传递。这两个标签是:<optional>和<exclusions>。
<optional>使用场景举例:
若依赖关系为:项目X->Y->Z,但Y中定义Z依赖为<optional>true</optional>。那么次定义不会对Y产生任何影响,但会对X产生影响。即依赖Z不会被传递到X中。如果你要在X中引用依赖Z,必须在X中明确定义。
<exclusions>使用场景举例:
若依赖关系为:项目X->Y->Z,但由于一系列原因,可能Z功能不在需要或Z依赖在仓库中丢失等等,总之,我们的X值依赖不在需要Z,不想要Z依赖通过Y传递到X中。我们可以在X中定义Y依赖如下,并排除Z:
<project> <modelVersion>4.0.0</modelVersion> <groupId>sample.ProjectA</groupId> <artifactId>X</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> ... <dependencies> <dependency> <groupId>sample.ProjectB</groupId> <artifactId>Y</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <exclusion> <groupId>sample.ProjectC</groupId> <!-- Exclude Project-D from Project-B --> <artifactId>Z</artifactId> </exclusion> </exclusions> </dependency> </dependencies></project>关于<optional>和<exclusions>的使用信息和示例,参考帮助文档:
http://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html