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,然后在各个项目中导入这些依赖管理配置。