1. 前言
我们知道,子模块可以通过继承获得父模块中声明的全部依赖,这样虽然避免了在各个子模块 POM 中重复进行依赖声明,但也极有可能造成子模块中引入一些不必要的依赖。为此 Maven 引入了 dependencyManagement 来对依赖进行管理。
2. 依赖管理
Maven 可以通过 dependencyManagement 元素对依赖进行管理,它具有以下 2 大特性:
- 在该元素下声明的依赖不会实际引入到模块中,只有在 dependencies 元素下同样声明了该依赖,才会引入到模块中。
- 该元素能够约束 dependencies 下依赖的使用,即 dependencies 声明的依赖若未指定版本,则使用 dependencyManagement 中指定的版本,否则将覆盖 dependencyManagement 中的版本。
例如,修改 App-Data-lib 模块的 pom.xml 如下。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--由于不是继承,所以必须重新添加 groupId 和 version-->
<groupId>net.wangzhe.www</groupId>
<artifactId>App-Data-lib</artifactId>
<version>1.0</version>
<!--dependencyManagement 标签用于控制子模块的依赖版本等信息 -->
<!-- 该标签只用来控制版本,不能将依赖引入 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<!--引用的properties标签中定义的属性 -->
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!--引用的properties标签中定义的属性 -->
<version>4.9</version>
<!-- <scope>test</scope> -->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--引用的properties标签中定义的属性 -->
<version>5.1.18</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<!--引用的properties标签中定义的属性 -->
<version>0.9.1</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--声明依赖-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
在以上配置中,由于 dependencyManagement 元素中已经定义完整的依赖声明,所以在 dependencies 元素中声明的依赖只配置了 groupId 和 artifactId,省略了 version 和 scope。
在实际的开发过程中,dependencyManagement 很少会单独使用,通常它需要与 Maven 继承或依赖范围 import 配合使用才能展现它的优势。
3. 继承依赖管理
由于 dependencyManagement 元素是可以被继承的,因此我们可以在父模块 POM 中使用 dependencyManagement 元素声明所有子模块的依赖,然后在各个子模块 POM 使用 dependencies 元素声明实际用到的依赖即可。这样既可以让子模块能够继承父模块的依赖配置,还能避免将不必要的依赖引入到子模块中。
- 修改父模块 Root 的 pom.xml 如下。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.wangzhe.www</groupId>
<artifactId>Root</artifactId>
<version>1.0</version>
<!--定义的父类pom.xml 打包类型使pom -->
<packaging>pom</packaging>
<properties>
<!-- 定义一些 maven 变量 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<log4j.version>1.2.17</log4j.version>
<junit.version>4.9</junit.version>
<system.version>1.0</system.version>
<mysql.connector.version>5.1.18</mysql.connector.version>
<c3p0.version>0.9.1</c3p0.version>
</properties>
<!--dependencyManagement 标签用于控制子模块的依赖版本等信息 -->
<!-- 该标签只用来控制版本,不能将依赖引入 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<!--引用的properties标签中定义的变量 -->
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!--引用的properties标签中定义的变量 -->
<version>${junit.version}</version>
<!-- <scope>test</scope> -->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--引用的properties标签中定义的变量 -->
<version>${mysql.connector.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<!--引用的properties标签中定义的变量 -->
<version>${c3p0.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
在父模块 Root 中使用 dependencyManagement 元素声明的依赖,既不会给 Root 模块引入依赖,也不会给其子模块引入依赖,但这段配置是可以被继承的。
- 修改子模块 App-Core-lib 的 pom.xml 如下。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>App-Core-lib</artifactId>
<parent>
<groupId>net.wangzhe.www</groupId>
<artifactId>Root</artifactId>
<version>1.0</version>
<relativePath>../Root</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>bianchengbang_Release_hosted</id>
<url>http://localhost:8082/nexus/content/repositories/bianchengbang_Release_hosted/</url>
</repository>
<snapshotRepository>
<id>Snapshot</id>
<url>http://localhost:8082/nexus/content/repositories/bianchengbang_Snapshot_hosted/</url>
</snapshotRepository>
</distributionManagement>
</project>
- 修改子模块 App-Data-lib 的 pom.xml 如下。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>App-Data-lib</artifactId>
<parent>
<groupId>net.wangzhe.www</groupId>
<artifactId>Root</artifactId>
<version>1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
在子模块 App-Core-lib 和 App-Data-lib 的 POM 中,所有在 dependencies 元素中声明的依赖都只配置了 groupId 和 artifactId,省去了 version 和 scope。之所以能够省略这些信息,是因为它们继承了父模块 Root 中 dependencyManagement 的配置,其完整的依赖声明已经包含在父模块的 POM 中,子模块只需要配置 groupId 和 artifactId 就能获得相应的依赖信息,从而引入正确的依赖。
使用这种依赖管理机制似乎并不能减少太多 POM 配置,但我们仍然推荐使用这种方式,其原因主要有 2 个:
- 在父模块中使用 dependencyManagement 声明依赖能够统一项目内依赖的版本,子模块无须声明版本,也就不会出现多个子模块使用同一依赖项版本不一致的情况,降低依赖冲突的几率。
- dependencyManagement 声明的依赖不会被实际引入,子模块需要什么依赖就自己引入,增加了灵活性,避免引入一些不必要的依赖。