我们开发一个很大的项目工程时,会用到很多模块或者子工程进行同步开发,如
CommonModel:公共的基础服务,比如工具类、常量类等等;
BussinessModel:业务模块,是系统真正要实现的业务,依赖于 common 模块,比如菜单管理、订单管理、权限管理、角色管理等;
Application:可发布的 web 应用,由各个 BussinessModel 组成,最终满足项目整体需求;
第三方模块(Library):包括各类框架,Spring、Springboot、SpringCloud等
Maven 管理多模块应用的实现是互联网项目中多使用分布式开发,每个独立的服 务都会使用独立的项目进行维护,那么这样就需要使用多模块应用管理,来实现项目的高度统一
Maven进行多模块工程管理主要有如下三种方式
一、创建空工程Empty Project,设置Parent父工程
1、创建一个空工程Empty Project
(1)File----New----Project,选择Empty Project,点击Next
(2)输入工程名,以及存放路径
Project name:项目名称
Project location:项目存放位置路径
2、创建Maven父工程
(1)File----New----Module
(2)选择Maven工程,是否选择模板创建,如果只是普通Java项目可直接下一步,不用勾选
maven-archetype-quickstart :
普通java项目(可选可不选,不选默认是普通Java工程)
(3)输入父工程,模块名,模块存放位置,以及模块坐标(gav)信息
Parent:父工程名(如果创建的工程本身就是父工程,这里选择None)
Name:子工程/模块名
Location:子工程/模块存放位置路径
GroupId:公司域名的倒序
ArtifactId:项目或模块名称
Version:项目或模块版本号
(4)配置导入设置
Maven 项目被修改后,需要“手动更新”或“自动更新”,通常选择右下角弹出框的
“Enable Auto-Import”
maven父工程必须遵循两点要求:
1、packaging标签的文本内容必须设置为pom
2、将src目录删除掉
(5)设置父工程的 pom.xml 文件
maven父工程必须遵循两点要求:
1、packaging标签的文本内容必须设置为pom
2、将src目录删除掉
此时可以看到 pom.xml 文件中内容如下
<modelVersion> 标签表示当前pom.xml所采用的标签结构是(从Maven2开始就固定为4.0.0)
<!-- 表示当前pom.xml所采用的标签结构是(从Maven2开始就固定为4.0.0) -->
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompay</groupId>
<artifactId>maven-modules-1-parent</artifactId>
<version>1.0.0</version>
<!--
maven父工程必须遵循两点要求:
1、packaging标签的文本内容必须设置为pom
2、将src目录删除掉
-->
<packaging>pom</packaging>
(6)删除 src 文件目录
如果有子工程\模块,会看到左上角有一个 m↓ 标记,点击选择可以跳转的子模块/工程
3、创建Maven子模块
(1)File----New----Module
(2)选择Maven工程,是否选择模板创建,如果只是普通Java项目可直接下一步,不用勾选
如果是web子模块,勾选如下
(3)选择父工程模块
Add as module to:选择将创建的模块添加到哪个模块下(老版本IDEA中有)
Parent:选择模块的父工程
GroupId:选择父工程后,默认继承父工程的 GroupId 值
ArtifactId:模块的项目名称
Version:选择父工程后,默认继承父工程的 Version 值
如果我们直接选择父工程,右键 “File----New----Module”,则这里会 parent 自动带出父工程名称
如果是2019版本IDEA,会有 Add as module to 这个选项,在这里选择None
博主当前使用的2021版本IDEA,因此没有 Add as module to 这个选项
在parent选择父工程之后,GroupId和Version会自动带出
如下是2021版IDEA
如下是2019版IDEA
web子模块同理
(4)模块目录视图
我们发现子模块的 pom.xml 文件左上角有一个 m↑ 这个标记,点击可跳转到父工程的 pom.xml 文件中
web子模块视图,packing标签多了一个 war
(5)子模块的 pom.xml 文件
<parent> 标签:
指向父工程
<relativePath> 标签:
相对路径(父工程的 pom.xml 文件相对路径)
除了子模块名可以修改,其他公司域名,version等都是继承父工程
<!--指向父工程的GAV坐标-->
<parent>
<artifactId>maven-modules-1-parent</artifactId>
<groupId>com.mycompany</groupId>
<version>1.0.0</version>
<relativePath>../maven-modules-1-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>maven-modules-1-java</artifactId>
4、创建子模块的下级子模块
按照上述创建子模块方式,选择父模块为上一级子模块即可
我们可看下创建后的视图
二、创建Maven父工程,设置 Add as module to 和 Parent
1、创建Maven父工程
(1)File----New----Project
(2)选择Maven工程,是否选择模板创建,如果只是普通Java项目可直接下一步,不用勾选(这里不用勾选,Java和web工程都无所谓)
(3)设置父工程项目名称和项目存放位置;并填写坐标(gav)信息
(4)配置导入设置
Maven 项目被修改后,需要“手动更新”或“自动更新”,通常选择右下角弹出框的
“Enable Auto-Import”
(5)将本maven工程修改为父工程
【1】设置 pom.xml 文件的 packing标签为 pom
【2】删除 src 目录
如果父工程下有子模块,在父工程的 pom.xml 文件
会有 <modules> 标签,如上,点击左上角的 m↓ 标记,可跳转到指定子模块工程
2、创建Maven子模块/工程
(1)File----New----Module
(2)选择Maven工程,Java工程可直接下一步
(3)选择父工程
Add as module to:选择将创建的模块添加到哪个模块下(老版本IDEA中有)
Parent:选择模块的父工程
GroupId:选择父工程后,默认继承父工程的 GroupId 值
ArtifactId:模块的项目名称
Version:选择父工程后,默认继承父工程的 Version 值
如果我们直接选择父工程,右键 “File----New----Module”,则这里会 parent 自动带出父工程名称
注:
如果是2019版本IDEA,会有 Add as module to 这个选项,在这里选择父工程,和 Parent 选项的父工程保持一致
博主当前使用的2021版本IDEA,因此没有 Add as module to 这个选项
在parent选择父工程之后,GroupId和Version会自动带出
如下是2021版IDEA
如下是2019版IDEA
(4)创建之后视图
子模块就在父工程目录之下了
同理,再创建一个web工程
此时,工程目录如下
我们再去看一下父工程的 pom.xml 文件
此时会多一个 <modules> 标签,如上,点击左上角的 m↓ 标记,可跳转到指定子模块工程
同理,我们也可以按照上述方式给子模块在创建子模块下的子模块(第三层,第四层......第N层等)
<modules> 聚合标签
指将多个模块整合在一起,统一构建,避免一个一个的构建。聚合需要个父工程,然后使用 <modules> 进行配置其中对应的是子工程的相对路径
聚合POM与继承中的父POM的packaging都必须是pom;同时,聚合模块与继承中的父模块除了POM外,都没有实际的内容
三、上述两种方式混合使用
如我们可以以第一种方式创建多个Maven父工程,再以第二种方式在每个父工程下创建子工程混合使用
四、设置父工程编译级别
Maven项目中会统一使用 JDK 版本和编译级别,将编 译插件添加到父工程,子模块依然会无条件去继承父工程的插件
1、查看编译级别
File ----> Settings ----> Build, Execution, Deployment ----> Compiler ----> Java Compiler
2、在父工程中的 pom.xml 文件 build ----> plugins 标签中添加编译插件
<build>
<plugins>
<!--JDK1.8编译插件-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<!-- 插件的版本 -->
<version>3.1</version>
<!-- 编译级别 -->
<configuration>
<source>1.8</source>
<target>1.8</target>
<!-- 编码格式 -->
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
如下
修改完成之后,我们点击右边工具栏的中的 Maven,点击 “刷新”按钮即可
此时再去查看,都变为 1.8版本了
五、管理依赖
Maven多模块管理的实质:实现依赖的版本统一管理
如果每个子模块都有自己依赖的jar包,且版本不同,那么在工程代码合并时会造成很大冲突,如子模块1 使用 mysql 5.1版本,而子模块2 使用 mysql 8.0版本,势必造成版本不统一,且有些参数,函数方法等会找不到等问题,那么合项目也是很大的困难
如下,我们在子模块maven-modules-1-java的 pom.xml文件 有Junit和 mysql5.1.49 依赖,刷新
在子模块 maven-modules-1-web的 pom.xml文件 有mysql8.0.19 依赖,刷新
这样就导致了两个子模块的 mysql 依赖版本不同,势必会出错
那么我们将子模块下的 pom.xml 文件里面的所有依赖都添加到 父工程下,删掉子模块下的 依赖<dependencies> 和 <dependency>标签
此时发现所有工程/模块(包括父工程,所有子模块)都有父工程 pom.xml 文件中添加的所有依赖jar包,如下
以上写做法,子模块会无条件继承父工程的所有依赖,导致的问题是,造成依赖 jar 包的 冗余 和 空间的浪费,本不需要的继承 的依赖也会被继承,这就大大增加了项目模块最终打包的大小,也可能未上线埋下了隐患。
父工程管理的是所有项目模块的依赖,而不是某一个项目模块的依赖,所以 某一个项目模块不需要继承父工程中的所有依赖,这就需要子项目模块向父工程声明需要的 依赖即可(声明式依赖)
此时,父工程实际只需要管理依赖的版本号即可
最终版本
1、在父工程添加依赖管理,使用 <dependencyManagement> 标签管理
子模块项目之前继承的依赖消失,由于父工程通过 dependencyManagement 标签管理依 赖,那么之前子模块无条件继承的依赖就全部消失
2、自定义标签名称
在 Maven 的 pom.xml 文件中,<properties>用于定义全局变量
POM 中通过${property_name}的形式引用变量的值,标签名称一般由 项目名称 + 字段version组成
单个单词可直接 XX.version命名,多个单词可横线分割 XX-XX-version
父工程 pom.xml 文件
<?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.mycompany</groupId>
<artifactId>maven-modules-1-parent</artifactId>
<version>1.0.0</version>
<!--
maven父工程必须遵循两点要求:
1、packaging标签的文本内容必须设置为pom
2、将src目录删除掉
-->
<packaging>pom</packaging>
<!--
packaging标签的是指定打包的方式
默认为jar,如果pom文件中没有packaging标签那么默认就是打jar
-->
<!--
pom是项目对象模型(Project Object Module),该文件是可以被子工程继承
maven多模块管理 其实就是让它的子模块的pom文件来继承父工程的pom文件
-->
<!-- 父管理依赖的版本号 -->
<properties>
<!--
自定义标签名称
在 Maven 的 pom.xml 文件中,<properties>用于定义全局变量
POM 中通过${property_name}的形式引用变量的值,标签名称一般由 项目名称 + 字段version组成
单个单词可直接 XX.version命名,多个单词可横线分割 XX-XX-version
-->
<junit.version>4.10</junit.version>
<mysql-connector-java-version>8.0.19</mysql-connector-java-version>
<dubbo.version>2.6.2</dubbo.version>
<spring-webmvc-version>4.3.16.RELEASE</spring-webmvc-version>
</properties>
<!--父工程要加强管理子模块的所有依赖-->
<dependencyManagement>
<!--父工程添加的依赖,所有子模块会无条件的继承-->
<dependencies>
<!-- junt依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!--JDK1.8编译插件-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<!-- 插件的版本 -->
<version>3.1</version>
<!-- 编译级别 -->
<configuration>
<source>1.8</source>
<target>1.8</target>
<!-- 编码格式 -->
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
3、子模块声明式添加依赖
由于父工程管理依赖的版本号,那么子模块要想继承依赖,只能通过声明式来添加依赖
实际上,子模块中的依赖是继承父工程依赖的版本号
如果子模块已定义依赖版本号,那么 以子模块定义的版本号为准
子工程 pom.xml 文件
<?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>maven-modules-1-parent</artifactId>
<groupId>com.mycompany</groupId>
<version>1.0.0</version>
<relativePath>../maven-modules-1-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>maven-modules-1-java</artifactId>
<!-- 设置packing为pom -->
<packaging>pom</packaging>
<!--
子模块可实现自己的依赖,但是默认不带版本号version,继承父类工程依赖版本号;
也可以修改自己的依赖版本号
-->
<dependencies>
<!-- 声明式依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
</project>
这时我们查看右方工具栏中的 Maven,可看到其他没写依赖的子模块,不会有依赖,只有声明要继承依赖的子模块才会有
<dependencyManagement> 标签
Maven中 dependencyManagement 标签使用
项目中多个模块间公共依赖的版本号、scope的控制
一个项目有很多模块,每个模块都会用到一些公共的依赖。这些公共的依赖若交由各个模块独自管理,若每个模块同一个依赖的版本号不一致,会造成很大问题
1、如 module-1 使用mysql5.7,module-2使用mysql8.0 ,那么mysql5.7和8.0之间有区别的特性,方法没法公用
2、打包和开发测试环境下对同一jar包不同版本号的处理可能不一致,造成运行时和测试时结果不一致项目升级时,会造成修改版本号时遍地开花的问题
dependencyManagement 标签通常适用于多模块环境下定义一个top module来专门管理公共依赖的情况
与dependencies标签下dependency的区别:
1、所有声明在dependencies里的依赖都会自动引入,并默认被所有的子项目继承dependencies
2、即使在子项目中不写该依赖项,那么子项目仍然会从父项目depenManagement中继承该artifactId和groupId依赖项(全部继承),若没有则报错。
3、dependencyManagement只是声明依赖的版本号,该依赖不会引入,因此子项目需要显示声明所需要引入的依赖,若子项目 中dependencies中的dependency声明了version,则父项目中dependencyManagement中的声明无效