在旅途 2019-10-06
这又是一个系列,一个要把Maven讲透的系列,希望能够对大家有帮助!
上一篇文章就聚合进行了详细的总结,通过聚合,解决了我们构建多个项目的繁琐问题。但是,通过总结上一篇文章,大家可能会发现这么个问题,Project-A和Project-B项目的POM文件,有很多相同的部分。通过以往的开发经验,如果多个模块有相同的部分,那就意味着我们可以把相同的部分抽取出来,作为公共的部分进行使用,比如在Java中,我们可以把相同的部分放在父类中,子类继承父类,就搞定了。在Maven的世界里,也有类似的机制能让我们提取出重复的配置,这就是POM的继承,而这篇文章就对Maven中的POM继承进行详细的总结。
这里我还是将通过一个例子来了解一下Maven继承的初步使用配置。还是使用三个工程项目Project-Parent、Project-C和Project-D来进行说明,三个项目关系如下:
Project-Parent工程POM文件如下:
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jellythink.ExtendDemo</groupId> <artifactId>Project-Parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Project-Parent</name> </project>
看这个POM文件,会发现和聚合有几分相像,只是没有modules
节点,同样需要注意的是packaging
的取值,必须使用pom。
Project-C的POM文件如下:
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.jellythink.ExtendDemo</groupId> <artifactId>Project-Parent</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../Project-Parent/pom.xml</relativePath> </parent> <artifactId>Project-C</artifactId> <name>Project-C</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.1.6.RELEASE</version> </dependency> </dependencies> </project>
Project-D的POM文件如下:
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.jellythink.ExtendDemo</groupId> <artifactId>Project-Parent</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../Project-Parent/pom.xml</relativePath> </parent> <artifactId>Project-D</artifactId> <name>Project-D</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.1.6.RELEASE</version> </dependency> </dependencies> </project>
在Project-C和Project-D工程中都使用了parent
元素声明父模块,parent
下的坐标元素groupId
、artifactId
和version
是必须的,它们指定了父模块的坐标;元素relativePath
表示父模块POM的相对路径。在项目构建时,Maven会首先根据relativePath
检查父POM。
同时,在Project-C和Project-D工程中,我们都没有在POM中指定groupId
和version
值,但是在构建时,依然可以成功构建。实际上,在Project-C和Project-D工程从父模块继承了这两个元素,这也就消除了一些不必要的配置。在这个例子中,子模块同父模块使用了同样的groupId
和version
值,如果遇到子模块需要使用和父模块不一样的groupId
或者version
的情况,我们则完全可以再子模块中显示声明。
在上面,可以看到groupId
和version
是可以被继承的,那么还有哪些POM元素可以被继承呢?这里我将一些我们经常用作继承的元素做一下汇总,并进行简单的说明:
groupId
:项目组IDversion
:项目版本distributionManagement
:项目的部署配置properties
:自定义的Maven属性dependencies
:项目的依赖配置dependencyManagement
:项目的依赖管理配置build
:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等上面说到dependencies
是可以被继承的,那我们在Project-C和Project-D工程中很多公共的部分就可以提取出来,统一放到Project-Parent工程中去了,这样就可以移除公共配置,简化配置。
上面说的做法是可行的,但是会存在问题。我们考虑一下这样的一个场景。Project-C和Project-D工程中公共的部分都提取到Project-Parent工程中去了,如果此时新增了一个Project-E工程,而Project-E工程有80%的依赖在Project-Parent工程中可以找到,另外那20%的依赖是个性化的;也就是说,Project-Parent工程中有20%的依赖对于Project-E工程来说是没有用的。这个时候如果Project-E工程继承Project-Parent工程,则会引入无用的依赖,违背了Maven的使用原则。对于这种场景,Maven中也有很完美的解决方案。
Maven提供的dependencyManagement
元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement
元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。现在将Project-Parent工程的POM文件修改如下:
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jellythink.ExtendDemo</groupId> <artifactId>Project-Parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Project-Parent</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <springframework.version>5.1.6.RELEASE</springframework.version> <junit.version>4.11</junit.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <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> </dependencies> </dependencyManagement> </project>
这里使用dependencyManagement,将springframework和junit依赖提取了出来,放到了Project-Parent工程中。这样声明的依赖既不会给Project-Parent引入依赖,也不会给它的子模块引入依赖,不过这段配置是会被继承的。现在,我们将Project-C工程的POM修改为以下内容:
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.jellythink.ExtendDemo</groupId> <artifactId>Project-Parent</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../Project-Parent/pom.xml</relativePath> </parent> <artifactId>Project-C</artifactId> <name>Project-C</name> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </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>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> </dependencies> </project>
Project-D项目和Project-C类似,这里不再累述。上面的POM中依赖配置比原来简单了不少,所有的springframework依赖只配置了groupId和artifactId,省去了version。这些依赖的配置都在Project-Parent工程中有了配置,子模块只需要简单的配置groupId和artifactId即可。如果在子模块不声明依赖的使用,即使该依赖已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果。
Maven提供了dependencyManagement元素帮助管理依赖,类似地,Maven也提供了pluginManagement元素帮助管理插件。同dependencyManagement一样,在pluginManagement元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。
到这里,关于Maven中的继承概念就总结完了。其实关于Maven中的聚合和继承,还有一些比较冷门的知识点,我这里没有总结,比如反应堆的裁剪等,如果比较感兴趣,可以自行去学习。
期待五一假期!
果冻想,玩代码,玩技术!
2019年4月27日,于内蒙古呼和浩特。