1、为什么要进行多模块的拆解
Maven多模块项目通过合理的模块拆分,实现代码的复用,便于维护和管理。尤其是一些开源框架,也是采用多模块的方式,提供插件集成,用户可以根据需要配置指定的模块。微服务项目是以多模块的方式开发,分包部署;
Maven多模块项目适用于大型的、复杂的,需要分层设计和模块化开发的项目。以下是适合采用Maven多模块项目的一些场景:
-
Web应用程序或服务端应用程序,特别是对于由多个子系统或模块组成的复杂应用程序。
-
基于微服务架构进行开发,其中每个服务都是一个独立的模块。
-
企业级应用程序,需要包含多个不同的子系统或模块,并且这些模块需要共享相同的依赖项和配置信息。
-
应用商店或插件式系统,其中每个应用程序或插件都可以作为一个单独的模块。
总之,如果要开发大型、复杂的应用程序,使用Maven多模块项目可以提高代码的可读性、可维护性和可扩展性,同时还可以更好地利用现有的开源库和工具来加速开发过程。
优势总结:
-
版本统一化管理
-
各项目各自独立,维护的时候方便
-
一个模块出现宕机,也不会影响其他模块
2、多模块拆解示例
前言
ERP项目通常包含多个业务模块,可以使用Maven的多模块项目结构来组织这些模块。以下是如何根据业务模块进行Maven多模块拆分的步骤:
- 创建父项目,作为所有模块的容器。
- 在父项目中定义所有子模块的配置。
- 创建各个业务模块作为子项目,并在它们的
pom.xml
中声明它们的父项目。
以下是示例代码:
父项目的pom.xml
:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yourcompany.erp</groupId>
<artifactId>erp-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>erp-module-inventory</module>
<module>erp-module-sales</module>
<module>erp-module-finance</module>
<!-- 其他模块 -->
</modules>
<!-- 父POM中的其他配置 -->
</project>
子模块的pom.xml
示例(以一个库模块为例):
<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/maven-v4_0_0.xsd">
<parent>
<groupId>com.yourcompany.erp</groupId>
<artifactId>erp-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>erp-module-inventory</artifactId>
<!-- 子模块的其他配置 -->
</project>
在这个例子中,我们创建了一个名为erp-parent
的父项目,它包含了三个子模块:
erp-module-inventory
(库存管理)
erp-module-sales
(销售管理)
erp-module-finance
(财务管理)
每个子模块都通过<parent>
元素声明它们的父项目,这样Maven就可以理解模块之间的层次关系。
3、 拆分规则
对于多模块Maven工程,其拆分规则应根据具体情况和项目需求来确定。以下是一些常用的拆分规则:
-
按业务模块划分: 将一个大型系统按照不同的业务模块进行划分,每个模块可以独立开发、测试、部署和维护。比如,对于一个电商系统,可以将商品管理、订单管理、用户管理等业务模块单独拆分成不同的子模块。
-
按功能模块划分: 将一个系统按照不同的功能模块进行划分,每个模块负责完成一个特定的功能,比如认证、授权、缓存、搜索等。
-
按技术组件划分: 将一个系统按照不同的技术组件进行划分,每个模块可以包含一个或多个技术组件,比如数据库连接、消息队列、缓存框架等。
-
按层次划分: 将一个系统按照不同的层次进行划分,每个模块可以包含一个或多个层次,比如数据访问层、服务层、控制层等。
-
混合划分: 可以将上述的任意两种或多种方式结合起来使用,以达到更好的代码复用、可维护性和可扩展性。
无论采用何种拆分规则,都需要注意以下几点:
拆分后的子模块应该具有独立的功能和职责,能够单独进行测试、构建和部署。
子模块之间应该避免出现循环依赖,否则会导致构建失败和部署异常。
子模块之间的接口应该设计良好,遵守面向接口编程的原则,以提高代码的复用性和灵活性。
4、 创建多模块的步骤
父项目要添加
<packaging>pom</packaging> 1.pom 2.jar 3.war
规则:
父工程
子工程
子子工程
5、 子模块没有启动类打包出错的解决办法
项目里包含的common或者utils之类的工具类子模块,只是提供给其他子模块引用,并不需要启动(没有main启动类)。但是父工程的pom文件引用了springboot的maven打包插件spring-boot-maven-plugin,即:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
父子工程中的pom.xml中去除该配置。
然后在父工程中添加如下插件:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
<properties>
<java.version>1.8</java.version>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</properties>
6、 导入依赖
依赖示例:
<properties>
<java.version>1.8</java.version>
<mybatis-plus.version>3.0.5</mybatis-plus.version>
<swagger.version>2.7.0</swagger.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>
<commons-io.version>2.6</commons-io.version>
<httpclient.version>4.5.1</httpclient.version>
</properties>
<dependencyManagement>
<dependencies>
<!--Spring Cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
7、Maven中依赖的域(了解)
在Maven中依赖的域有: compile、provided、runtime、system、test、import。
依赖范围(Scope) | 编译classpath | 测试classpath | 运行时classpath | 传递性 | 说明 |
---|---|---|---|---|---|
compile | Y | Y | Y | Y | spring-core |
test | - | Y | - | - | junit |
provided | Y | Y | - | - | servlet-api |
runtime | - | Y | Y | Y | jdbc驱动 |
system | Y | Y | - | - | 本地jar包 |
- compile(默认)
当依赖的scope为compile的时候,那么当前这个依赖的包,会在编译的时候被加入进来,并且在打包(mvn package)的时候也会被加入进来。
编译范围有效,在编译与打包时都会加入进去。
- test
当依赖的scope为test的时候,指的的是在测试范围有效,在编译与打包的时候都不会使用这个依赖。
- provided
当依赖的scope为provided的时候,在编译和测试的时候有效,在执行(mvn package)进行打包时不会加入。比如, 我们开发一个web应用,在编译时我们需要依赖servlet-api.jar,但是在运行时我们不需要该 jar包,因为这个jar 包已由web服务器提供,如果在打包时又被加入进去,那么就可能产生冲突。此时我们就可以使用 provided 进行范围修饰。
- runtime
当依赖的scope为runtime的时候,在运行的时候才会依赖,在编译的时候不会依赖。比如,在编译的时候我们不需要JDBC API的jar包,而在运行的时候我们才需
要JDBC驱动包。就可以使用runtime修饰。
- system
与provided相同,不过被依赖项不会从maven仓库获取,而是从本地文件系统拿,需要配合systemPath属性使用。比如:
<dependency>
<groupId>org.open</groupId>
<artifactId>open-core</artifactId>
<version>1.5</version>
<scope>system</scope>
<systemPath>${basedir}/WebContent/WEB-INF/lib/open-core.jar</systemPath>
</dependency>
- import
maven多模块项目结构中,可以使用parent 定义父项目,实现从父项目中继承依赖。但maven只能单继承,即一个项目只能使用一个parent标签定义父项目。
maven2.9之后的版本引入了一个新的功能,可以实现依赖上的多重继承。这个功能可以将依赖配置复杂的pom文件拆分成多个独立的pom文件。这样处理
可以使得maven的pom配置更加简洁,同时可以复用这些pom依赖。
比如,我们在开发spring boot 项目的时候,pom中会有如下配置:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
但是,如果该项目是个maven子模块的话,就会出现问题。由于maven类似java是单继承,不能有两个parent,现在标签已经用来引用父
模块,现在又用来引用springboot,就会产生冲突。
那解决办法就是: 使用dependencyManagement引入dependency,并且把scope属性改为import。如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
注意:scope的import 属性只能在 中使用,表示从其它的pom文件中导入dependency配置。
8、idea同时启动多个服务模块
进行配置将以下代码写入.idea目录下的workspase.xml的RunDashboard节点下
<option name="configurationTypes">
<set>
<option value="SpringBootApplicationConfigurationType" />
</set>
</option>
配置完成后,重启idea
<component name="RunDashboard">
<option name="configurationTypes">
<set>
<option value="SpringBootApplicationConfigurationType" />
</set>
</option>
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>