maven的聚合和继承详解(2021版)
Posted 于大圣
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了maven的聚合和继承详解(2021版)相关的知识,希望对你有一定的参考价值。
前言:日常开发中,两处常见的项目开发场景:多模块项目或者Springboot项目,都会用到Maven的聚合和继承,本篇博客就针对maven这两个技术点进行总结整理,希望能对你有所帮助!
在进入正题之前,先看一下如下项目结构erms作为父模块或者聚合模块,下面有8个模块,每个模块都有自己单独的pom.xml文件。来思考以下两个问题:
- 如果要打包部署war包(简单点就是一次构建多个项目)的话,岂不是需要到每个不同的模块下执行mvn命令来完成打包操作?
- 如果不同模块需要依赖相同的jar包,难道需要在需要的模块下分别编写依赖吗?如果依赖或者依赖版本改动的话,岂不是需要改动多处?
一、聚合
聚合的出现主要是为了方便快速构建项目。比如我们在实际项目开发中会应用分层成多个maven项目,传统模式下我们需要按照一定的顺序去不同的maven项目下执行mvn命令,这种重复机械性的操作给开发带来了诸多不便。Maven聚合(或称为多模块)这一特性就时允许一次构建多个项目。
一般来说,为了方便快速定位内容,模块所处的目录名称应当与其artifactId一致;
通常将聚合模块放在项目目录的最顶层,其它模块作为聚合模块的子目录存在;聚合模块仅仅是帮助聚合其它模块构建的工具,它本身并无实质的内容
二、继承
2.1 概念在聚合部分中,我们知道一个项目可以是多模块项目,如果不同模块中有许多相同的配置,比如相同的groupId和version,相同的依赖和插件配置,即在不同的pom文件中出现重复的内容,重复往往意味着更多的劳动和潜在的问题。在面向对象编程中,我们可以通过类的继承机制一定程度上解决重复问题。在maven中同样可以通过pom的继承抽取出重复的配置来解决重复;
简单来说就是创建一个父pom文件并声明一些配置,供子类继承;Maven的继承机制只是为了消除配置的重复,子模块可以通过以下配置来实现继承
<parent>
<artifactId>hzrj-aggregation</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
2.2 可继承内容:那么子pom可以从父pom继承哪些内容呢,如下所示,大家简单记录下常用即可:
- groupId :项目组 ID ,项目坐标的核心元素;
- version :项目版本,项目坐标的核心元素;
- description :项目的描述信息;
- organization :项目的组织信息;
- inceptionYear :项目的创始年份;
- url :项目的 url 地址
- develoers :项目的开发者信息;
- contributors :项目的贡献者信息;
- distributionManagerment :项目的部署信息;
- issueManagement :缺陷跟踪系统信息;
- ciManagement :项目的持续继承信息;
- scm :项目的版本控制信息;
- mailingListserv :项目的邮件列表信息;
- properties :自定义的 Maven 属性;
- dependencies :项目的依赖配置;
- dependencyManagement :醒目的依赖管理配置;
- repositories :项目的仓库配置;
- build :包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等;
- reporting :包括项目的报告输出目录配置、报告插件配置等。
2.3 当我们利用继承将子模块中所依赖的所有内容都放到父POM中后又延伸出一个新的问题:这样会导致父POM中的依赖是所有子模块的并集(假设有30个),即包含所有子模块依赖,这就导致子模块在继承父POM后会继承父POM中所有依赖,但子模块可能只需要其中少数几个依赖(假设10个),但却依赖了大量自己不需要的依赖;对此有两种思路:
类似面向对象的编程中父POM只配置所有子模块依赖的交集,子模块独有的依赖由子模块单独配置。这种方式是可行的,但比如有A,B,C三个模块,最开始只有模块A依赖junit,到了后来模块B也依赖junit,那么这个时候就可能需要在模块B中引入junit依赖。相当于在模块A和B中都需要配置groupId、artifactId和version等,每次junit依赖改动(比如版本升级或者artifactId变动)都需要修改对应模块的依赖部分内容;
针对上述问题,比较好的一种思路是:父POM中统一配置所有依赖的完整声明,子模块在没有显示引入依赖时不会引入实际依赖,只有子模块在dependencies下通过dependency元素才会正式引入依赖;既保证子模块灵活使用依赖的同时,也约束了依赖的使用;对此Maven提供了dependencyManagement元素来提供上述功能,如下所示,我们在父POM进行如下配置,虽然dependencyManagement配置了几个依赖,但并不会引入任何依赖,只是起到了依赖的完整声明。
<properties>
<junit-version>4.12</junit-version>
<springframework-version>4.3.7.RELEASE</springframework-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mybatis-version>3.4.1</mybatis-version>
<mybatis-spring-version>1.3.0</mybatis-spring-version>
<hibernate-validator-version>5.4.1.Final</hibernate-validator-version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>$junit-version</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>$mybatis-version</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>$mybatis-spring-version</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>$hibernate-validator-version</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>$springframework-version</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>$springframework-version</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>$springframework-version</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>$springframework-version</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>$springframework-version</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>$springframework-version</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
接下来我们只需要在子POM文件中做如下配置即可引入依赖,最终实际引入的依赖只有junit和mybatis,由于junit和mybatis的version和scope属性已在父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">
<parent>
<artifactId>hzrj-aggregation</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hzrj-submodule</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
</dependencies>
</project>
扩展:大家都知道一个Springboot项目的POM文件,一般都采用Maven继承,即通过parent关键字继承自官方提供的spring-boot-starter-parent:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
但比如当前项目已经有了父POM,但又想使用spring-boot提供的功能,由于Maven是单继承,那么对此可以使用dependencyManagement导入的用法来解决,以下配置的作用是:将spring-boot-dependencies POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中,注意import这个依赖范围,这个依赖范围只在dependencyManagement元素下才有作用,且它所指向的依赖通常是一个打包类型为POM的模块;
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
三、聚合和继承的关系
聚合是为了方便快速构建项目,而继承是为了消除重复配置。实际项目中并不会严格地把聚合和继承分开,往往会发现一个POM既是聚合POM,又是继承POM。无论如何变化记住本质即可。
以上,完了!
以上是关于maven的聚合和继承详解(2021版)的主要内容,如果未能解决你的问题,请参考以下文章