8.3.3 依赖管理

上一节的可继承元素列表包含了dependencies元素,说明依赖是会被继承的,这时我们就会很容易想到将这一特性应用到account-parent中。子模块account-email和account-persist同时依赖了org.springframework:spring-core:2.5.6、org.springframework:spring-beans:2.5.6、org.springframework:spring-context:2.5.6和junit:junit:4.7,因此可以将这些依赖配置放到父模块account-parent中,两个子模块就能移除这些依赖,简化配置。

上述做法是可行的,但却存在问题。到目前为止,我们能够确定这两个子模块都包含那四个依赖,不过我们无法确定将来添加的子模块就一定需要这四个依赖。假设将来项目中需要加入一个account-util模块,该模块只是提供一些简单的帮助工具,与springframework完全无关,难道也让它依赖spring-core、spring-beans和spring-context吗?那显然是不合理的。

Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。例如,可以在account-parent中加入这样的dependencyManagement配置,见代码清单8-14。

代码清单8-14 在account-parent中配置dependencyManagement元素


<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/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.juvenxu.mvnbook.account</groupId>

<artifactId>account-parent</artifactId>

<version>1.0.0-SNAPSHOT</version>

<packaging>pom</packaging>

<name>Account Parent</name>

<properties>

<springframework.version>2.5.6</springframework.version>

<junit.version>4.7</junit.version>

</properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context-support</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

<scope>test</scope>

</dependency>

</dependencies>

</dependencyManagement>

</project>


首先该父POM使用了5.9.2节介绍的方法,将springframework和junit依赖的版本以Maven变量的形式提取了出来,不仅消除了一些重复,也使得各依赖的版本处于更加明显的位置。

这里使用dependencyManagement声明的依赖既不会给account-parent引入依赖,也不会给它的子模块引入依赖,不过这段配置是会被继承的。现在修改account-email的POM如下,见代码清单8-15。

代码清单8-15 继承了dependencyManagement的account-email POM


<properties>

<javax.mail.version>1.4.1</javax.mail.version>

<greenmail.version>1.3.1b</greenmail.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context-support</artifactId>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

</dependency>

<dependency>

<groupId>javax.mail</groupId>

<artifactId>mail</artifactId>

<version>${javax.mail.version}</version>

</dependency>

<dependency>

<groupId>com.icegreen</groupId>

<artifactId>greenmail</artifactId>

<version>${greenmail.version}</version>

<scope>test</scope>

</dependency>

</dependencies>


上述POM中的依赖配置较原来简单了一些,所有的springframework依赖只配置了groupId和artifactId,省去了version,而junit依赖不仅省去了version,还省去了依赖范围scope。这些信息可以省略是因为account-email继承了account-parent中的dependencyManage-ment配置,完整的依赖声明已经包含在父POM中,子模块只需要配置简单的groupId和artifactId就能获得对应的依赖信息,从而引入正确的依赖。

使用这种依赖管理机制似乎不能减少太多的POM配置,不过笔者还是强烈推荐采用这种方法。其主要原因在于在父POM中使用dependencyManagement声明依赖能够统一项目范围中依赖的版本,当依赖版本在父POM中声明之后,子模块在使用依赖的时候就无须声明版本,也就不会发生多个子模块使用依赖版本不一致的情况。这可以帮助降低依赖冲突的几率。

如果子模块不声明依赖的使用,即使该依赖已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果,如account-persist的POM,见代码清单8-16。

代码清单8-16 继承了dependencyManagement的account-persist POM


<properties>

<dom4j.version>1.6.1</dom4j.version>

</properties>

<dependencies>

<dependency>

<groupId>dom4j</groupId>

<artifactId>dom4j</artifactId>

<version>${dom4j.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

</dependency>

</dependencies>


这里没有声明spring-context-support,那么该依赖就不会被引入。这正是dependencyManagement的灵活性所在。

5.5节在介绍依赖范围的时候提到了名为import的依赖范围,推迟到现在介绍是因为该范围的依赖只在dependencyManagement元素下才有效果,使用该范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。例如想要在另外一个模块中使用与代码清单8-14完全一样的dependencyManagement配置,除了复制配置或者继承这两种方式之外,还可以使用import范围依赖将这一配置导入,见代码清单8-17。

代码清单8-17 使用import范围依赖导入依赖管理配置


<dependencyManagement>

<dependencies>

<dependency>

<groupId>com.juvenxu.mvnbook.account</groupId>

<artifactId>account-parent</artifactId>

<version>1.0-SNAPSHOT</version>

<type>pom</type>

<scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>


注意,上述代码中依赖的type值为pom,import范围依赖由于其特殊性,一般都是指向打包类型为pom的模块。如果有多个项目,它们使用的依赖版本都是一致的,则就可以定义一个使用dependencyManagement专门管理依赖的POM,然后在各个项目中导入这些依赖管理配置。