面向对象设计中,程序员可以建立一种类的父子结构,然后在父类中声明一些字段和方法供子类继承,这样就可以做到“一处声明,多处使用”。类似地,我们需要创建POM的父子结构,然后在父POM中声明一些配置供子POM继承,以实现“一处声明,多处使用”的目的。
我们继续以账户注册服务为基础,在account-aggregator下创建一个名为account-parent的子目录,然后在该子目录下建立一个所有除account-aggregator之外模块的父模块。为此,在该子目录创建一个pom.xml文件,内容见代码清单8-10。
代码清单8-10 account-parent的POM
该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
上述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
最后,同样还需要把account-parent加入到聚合模块account-aggregator中,见代码清单8-13。
代码清单8-13 将account-parent加入到聚合模块