maven进阶:基础知识

时间:2023-02-14 17:24:40

什么是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>
<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>
若两个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>  <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中。

更多示例请参考官方文档。地址:

http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies

导入依赖小结

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