最近在负责的是一个比较复杂项目,模块很多,代码中的二级模块就有 9 个,部分二级模块下面还分了多个模块。代码中的多模块是用 maven 管理的,每个模块都使用 spring boot 框架。之前有零零散散学过一些 maven 多模块配置的知识,但没自己从头到尾创建和配置过,也快忘得差不多了。这次正好对照着这个项目,动手实践一下,下面我们就开始吧。
maven 多模块项目通常由一个父模块和若干个子模块构成,每个模块都对应着一个 pom.xml。它们之间通过继承和聚合(也称作多模块)相互关联。多模块适用于一些比较大的项目,通过合理的模块拆分,实现代码的复用,便于维护和管理。
因为本系列的下一篇是《Spring Boot 集成 Dubbo》,所以本章就以创建多模块的 dubbo 项目作为示例。示例中的开发环境是 Win 7,编辑器是 Intellij IDEA,Java 版本是 1.8。
首先我们在 IDEA 中创建一个 spring boot 工程作为父项目。
一、在界面左上角选择 File->New->Project 后,选择 Spring Initializr,默认使用的 Java 版本是 1.8。
二、点击 Next,进入下一步,可以设置项目的一些基本信息。
这里我们先来温习下 groupId、artifactId、package 这三个参数的一般填写规范。
groupId 和 artifactId 统称为 “坐标”,是为了保证项目唯一性而提出的。groupId 是项目组织唯一的标识符,实际对应 JAVA 的包的结构,ArtifactID 是项目的唯一的标识符,实际对应项目的名称,就是项目根目录的名称。groupId 一般分为多个段,一般第一段为域,第二段为公司名称。举个 apache 公司的 tomcat 项目例子:这个项目的 groupId 是 org.apache,它的域是 org,公司名称是 apache,artifactId 是 tomcat。包结构 package 最好是以 groupId.artifactId 打头的。
因为后续打算将 “代码学习和实践” 写成一个系列的文章,文中演示的工程都作为该工程的子模块,所以这里项目名 Name 就填写 CodeLearnAndPractice。
这里是个人练习的项目,不涉及公司名,但 groupId、artifactId、package 参数的填写,还是尽量按照上面的规范来填写,这里 package 就直接用 groupId.artifactId。如下所示:
三、点击 Next,进入下一个选择 dependency 的界面,作用是在 pom 中自动添加一些依赖,在项目开始时就下载。这里我们暂时不勾选任何依赖。
四、点击 Next,进入下一个界面,填写工程名,并选择工程所在目录。填写完成后,点击 Finish,即可创建一个 spring boot 项目。
在上面创建好的 CodeLearnAndPractice 工程名上,点击右键,选择 New–>Module,进入 New Module 页面。
该模块为 dubbo 服务的提供方,Name 为 springboot-dubbo-server,后面其他的参数都可参照父模块的参数设置。
下面创建另一个 Module,dubbo 服务的调用方,Name 为 springboot-dubbo-client,其他参数设置参照上步。
以上 3 个模块创建完成之后,整个项目的目录结构如下图所示。
我们把下图选中的无用的文件及文件夹删掉,包括三个模块的 mvnw、mvnw.cmd 文件及.mvn 文件夹,还有父模块的 src 目录,因为此处的父模块只做依赖管理,不需要编写代码。
到这里,一个父模块和两个子模块都创建完成啦~~
父 pom 是为了抽取统一的配置信息和依赖版本控制,方便子 pom 直接引用,简化子 pom 的配置。
下面介绍下父 pom 的配置中需要注意的一些地方。我贴出的 pom 看起来会有点冗余,因为其中一些不需要的地方,我没有直接删掉而是注释掉,并加了说明,是为了后续查看的时候还能清楚删掉的原因。
多模块项目中,父模块打包类型必须是 pom,同时以给出所有的子模块,其中每个 module,都是另外一个 maven 项目。
我们的项目中目前一共有两个子模块,springboot-dubbo-server 和 springboot-dubbo-client。后续新增的子模块也必须加到父 pom 的 modules 中。
继承是 maven 中很强大的一种功能,继承可以使子 pom 获得 parent 中的各项配置,对子 pom 进行统一的配置和依赖管理。父 pom 中的大多数元素都能被子 pom 继承,想深入了解的同学可自行搜索学习~~
maven 项目之间的继承关系通过表示。这里使用的开发框架是 spring boot,默认继承 spring-boot-starter-parent。
一般在项目最顶层的父 pom 中使用该元素,让所有子模块引用一个依赖而不用显式的列出版本号。maven 会沿着父子层次向上走,直到找到一个拥有 dependencyManagement 元素的项目,然后它就会使用在这个 dependencyManagement 元素中指定的版本号。
在 properties 标签中,添加各依赖包的版本号,然后在 dependency 中直接引用该依赖版本号的值即可。
CodeLearnAndPractice/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.practice</groupId>
<artifactId>CodeLearnAndPractice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!--<packaging>jar</packaging>-->
<packaging>pom</packaging> <!--父模块打包类型必须为pom-->
<modules>
<module>springboot-dubbo-server</module>
<module>springboot-dubbo-client</module>
</modules>
<name>CodeLearnAndPractice</name>
<description>Practice the learned code</description>
<!-- parent指明继承关系,给出被继承的父项目的具体信息-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- 在properties中统一控制依赖包的版本,更清晰-->
<dubbo.version>2.5.3</dubbo.version>
<zkclient.version>0.10</zkclient.version>
</properties>
<dependencyManagement> <!--dependencyManagement用于管理依赖版本号-->
<dependencies>
<!-- 删除spring-boot-starter和spring-boot-starter-test,
因为parent中继承的祖先中已经有了,并且一般dependencyManagement管理的依赖都要写版本号 -->
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-test</artifactId>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<!--新增后续dubbo项目中所需依赖,dubbo、zk-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<!--<version>2.5.3</version>--> <!--使用properties中配置的版本号-->
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<!--<version>0.10</version>--> <!--使用properties中配置的版本号-->
<version>${zkclient.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--该插件作用是打一个可运行的包,必须要写在需要打包的项目里。这里的父模块不需要打包运行,所以删掉该插件。-->
<!--<build>-->
<!--<plugins>-->
<!--<plugin>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-maven-plugin</artifactId>-->
<!--</plugin>-->
<!--</plugins>-->
<!--</build>-->
</project>
子模块的 parent 要使用顶层的父模块.
父模块 pom 中使用 dependencyManagement 来管理的依赖,在子模块 pom 中就不需要再写版本号了,exclusion 元素也不需要再写。
springboot-dubbo-server\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.practice</groupId>
<artifactId>springboot-dubbo-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-dubbo-server</name>
<description>Demo project for Spring Boot</description>
<!-- 子模块的parent要使用顶层的父模块-->
<parent>
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-parent</artifactId>-->
<!--<version>1.5.8.RELEASE</version>-->
<!--<relativePath/>-->
<groupId>com.practice</groupId>
<artifactId>CodeLearnAndPractice</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<!-- properties可删掉,会继承父模块的-->
<!--<properties>-->
<!--<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->
<!--<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>-->
<!--<java.version>1.8</java.version>-->
<!--</properties>-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--新增后续dubbo项目中所需依赖,dubbo、zk。
父模块pom中使用dependencyManagement来管理依赖版本号,子模块pom中不需要再写版本号,exclusion也不需要-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<!--<version>2.5.3</version>-->
<!--<exclusions>-->
<!--<exclusion>-->
<!--<groupId>org.springframework</groupId>-->
<!--<artifactId>spring</artifactId>-->
<!--</exclusion>-->
<!--</exclusions>-->
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<!--<version>0.10</version>-->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
springvoot-dubbo-client/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.practice</groupId>
<artifactId>springboot-dubbo-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-dubbo-client</name>
<description>Demo project for Spring Boot</description>
<!-- 子模块的parent要使用顶层的父模块-->
<parent>
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-parent</artifactId>-->
<!--<version>1.5.8.RELEASE</version>-->
<!--<relativePath/>-->
<groupId>com.practice</groupId>
<artifactId>CodeLearnAndPractice</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<!-- properties可删掉,会继承父模块的-->
<!--<properties>-->
<!--<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->
<!--<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>-->
<!--<java.version>1.8</java.version>-->
<!--</properties>-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 该模块需要启动web服务,需要该依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--新增后续dubbo项目中所需依赖,dubbo、zk
父模块pom中使用dependencyManagement来管理依赖版本号,子模块pom中不需要再写版本号
父模块pom中里有exclusion,子模块pom中不要写exclusion-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<!--<version>2.5.3</version>-->
<!--<exclusions>-->
<!--<exclusion>-->
<!--<groupId>org.springframework</groupId>-->
<!--<artifactId>spring</artifactId>-->
<!--</exclusion>-->
<!--</exclusions>-->
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<!--<version>0.10</version>-->
</dependency>
<!--client模块需要依赖server模块-->
<dependency>
<groupId>com.practice</groupId>
<artifactId>springboot-dubbo-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
当 dependency A 自身的依赖 B,与其他 dependency 存在冲突的时候(最常见的就是版本冲突),我们就需要把 B 排除掉,这时就需要使用 exclusions 元素。
那么我们怎么知道一个 dependency 自身包含哪些依赖呢?
1、通过 mvn dependency:tree 命令查看依赖树
2、使用 IDEA 或其他 IDA 查看依赖树
点击 IDEA 右侧的 Maven Projects,在每个模块的 Dependencies 中即可查看每个 dependency 内部的依赖及版本号,从来识别哪些依赖需要被排除掉。
以 dubbo 为例,我们先删除配置,点开 Maven Projects,可以看到 2.5.3 版本的 dubbo 中使用的 spring 版本是 2.5.6,这是一个很老的版本,有一些方法是没有的,现在在用的 spring 版本一般都是 4.* 的,所以我们需要把它排除掉,避免后续报错。
要查看当前项目中使用的 spring 版本,可以按住左键,然后点击父 pom中的值,进入更上一层 pom,再重复上步操作,可以看到 spring 的版本是 4.3.12。
按住左键,然后点击父 pom中的值,进入更上一层 pom:
可以看到 spring 的版本是 4.3.12:
这里就先不写代码了,到下一章再写。直接编译一下,如果编译成功,说明 pom 文件的配置没有什么大问题。
点开右侧 Maven Projects,双击父模块 Lifecycle 中的 compile,进行代码编译,或者直接在 Terminal 中执行命令:mvn compile。
编译通过啦~~
到这里,Spring Boot 多模块项目创建与配置就介绍完啦。
这样一个过程实践下来,再去看开发的代码结构,应该会轻松不少吧~~~
Maven 学习 (六) 搭建多模块企业级项目
基于 maven 使用 IDEA 创建多模块项目
第二十二章:SpringBoot 项目多模块运用与设计
[摘录] Maven 中的 DependencyManagement 和 Dependencies
代码学习与实践:开篇 - 测试深入了解代码的好处及实践
代码学习与实践 (一):Spring Boot 多模块项目创建与配置
代码学习与实践 (二):Spring Boot 集成 Dubbo
github 代码地址:https://github.com/Tester-Ella/CodeLearnAndPractic