8.3 继承

到目前为止,我们已经能够使用Maven的聚合特性通过一条命令同时构建account-email和account-persist两个模块,不过这仅仅解决了多模块Maven项目的一个问题。那么多模块的项目还有什么问题呢?

细心的读者可能已经比较过5.3.1节和8.1.1节,这两个POM有着很多相同的配置,例如它们有相同的groupId和version,有相同的spring-core、spring-beans、spring-context和junit依赖,还有相同的maven-compiler-plugin与maven-resources-plugin配置。程序员的嗅觉对这种现象比较敏感,没错,这是重复!大量的前人经验告诉我们,重复往往就意味着更多的劳动和更多的潜在的问题。在面向对象世界中,程序员可以使用类继承在一定程度上消除重复,在Maven的世界中,也有类似的机制能让我们抽取出重复的配置,这就是POM的继承。

8.3.1 account-parent

面向对象设计中,程序员可以建立一种类的父子结构,然后在父类中声明一些字段和方法供子类继承,这样就可以做到“一处声明,多处使用”。类似地,我们需要创建POM的父子结构,然后在父POM中声明一些配置供子POM继承,以实现“一处声明,多处使用”的目的。

我们继续以账户注册服务为基础,在account-aggregator下创建一个名为account-parent的子目录,然后在该子目录下建立一个所有除account-aggregator之外模块的父模块。为此,在该子目录创建一个pom.xml文件,内容见代码清单8-10。

代码清单8-10 account-parent的POM


<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>

</project>


该POM十分简单,它使用了与其他模块一致的groupId和version,使用的artifactId为account-parent表示这是一个父模块。需要特别注意的是,它的packaging为pom,这一点与聚合模块一样,作为父模块的POM,其打包类型也必须为pom。

由于父模块只是为了帮助消除配置的重复,因此它本身不包含除POM之外的项目文件,也就不需要src/main/java/之类的文件夹了。

有了父模块,就需要让其他模块来继承它。首先将account-email的POM修改如下,见代码清单8-11。

代码清单8-11 修改account-email继承account-parent


<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>

<parent>

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

<artifactId>account-parent</artifactId>

<version>1.0.0-SNAPSHOT</version>

<relativePath>../account-parent/pom.xml</relativePath>

</parent>

<artifactId>account-email</artifactId>

<name>Account Email</name>

<dependencies>

……

</dependencies>

<build>

<plugins>

……

</plugins>

</build>

</project>


上述POM中使用parent元素声明父模块,parent下的子元素groupId、artifactId和version指定了父模块的坐标,这三个元素是必须的。元素relativePath表示父模块POM的相对路径,该例中的../account-parent/pom.xml表示父POM的位置在与account-email/目录平行的account-parent/目录下。当项目构建时,Maven会首先根据relativePath检查父POM,如果找不到,再从本地仓库查找。relativePath的默认值是../pom.xml,也就是说,Maven默认父POM在上一层目录下。

正确设置relativePath非常重要。考虑这样一个情况,开发团队的新成员从源码库签出一个包含父子模块关系的Maven项目。由于只关心其中的某一个子模块,它就直接到该模块的目录下执行构建,这个时候,父模块是没有被安装到本地仓库的,因此如果子模块没有设置正确的relativePath,Maven将无法找到父POM,这将直接导致构建失败。如果Maven能够根据relativePath找到父POM,它就不需要再去检查本地仓库。

这个更新过的POM没有为account-email声明groupId和version,不过这并不代表account-email没有groupId和version。实际上,这个子模块隐式地从父模块继承了这两个元素,这也就消除了一些不必要的配置。在该例中,父子模块使用同样的groupId和version,如果遇到子模块需要使用和父模块不一样的groupId或者version的情况,那么用户完全可以在子模块中显式声明。对于artifactId元素来说,子模块应该显式声明,一方面,如果完全继承groupId、artifactId和version,会造成坐标冲突;另一方面,即使使用不同的groupId或version,同样的artifactId容易造成混淆。

为了节省篇幅,上述POM中省略了依赖配置和插件配置,稍后本章会介绍如何将共同的依赖配置提取到父模块中。

与account-email的POM类似,以下是account-persist更新后的POM,见代码清单8-12。

代码清单8-12 修改account-persist继承account-parent


<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>

<parent>

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

<artifactId>account-parent</artifactId>

<version>1.0.0-SNAPSHOT</version>

<relativePath>../account-parent/pom.xml</relativePath>

</parent>

<artifactId>account-persist</artifactId>

<name>Account Persist</name>

<dependencies>

……

</dependencies>

<build>

<testResources>

<testResource>

<directory>src/test/resources</directory>

<filtering>true</filtering>

</testResource>

</testResources>

<plugins>

……

</plugins>

</build>

</project>


最后,同样还需要把account-parent加入到聚合模块account-aggregator中,见代码清单8-13。

代码清单8-13 将account-parent加入到聚合模块


<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-aggregator</artifactId>

<version>1.0.0-SNAPSHOT</version>

<packaging>pom</packaging>

<name>Account Aggregator</name>

<modules>

<module>account-parent</module>

<module>account-email</module>

<module>account-persist</module>

</modules>