大厂Java项目如何进行Maven多模块管理
什么是多模块管理
多模块管理简单地理解就是一个 Java 工程项目中不止有一个 pom.xml 文件,会在不同的目录中有多个这样的文件,进而实现 Maven 的多模块管理
为什么要使用多模块管理
随着业务的增长,代码会出现以下问题:
- 不同业务之间的代码互相耦合,难以区分且快速定位问题
- 增加开发成本,入手难度增高
- 开发界线模糊,不易定位到具体负责人
- 对于有特殊需求的模块无法拆解,比如:上传 maven 仓库只需要部分代码即可,但由于只有 1 个模块,不得不全部上传
故而拆分模块之后,可以避免上述问题
模块拆分方案
通常拆分有 2 种方案
按照结构拆分
- project
- project-service
- project-controller
- project-dao
按照业务拆分
- project
- project-order
- project-account
- project-pay
实际项目结构
以一个普通 Spring Boot 项目为例,首先放一张图,看一下整体项目完成后的结构
其中目录结构为
- detail-page
- detail-client
- detail-service
- detail-start
- detail-client 用于放需要打包传到 maven 库的代码
- detail-service 用于放置主要的业务逻辑代码
- detail-start 用于放启动代码
其中需要注意的是 pom.xml 的文件的配置,该配置决定了父子模块之间的关系
1、detail-page 的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.drawcode</groupId>
<artifactId>detail-page</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging> <!-- 此处必须为pom -->
<name>detail-page</name>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!-- modules即为父子关系 -->
<modules>
<module>detail-client</module>
<module>detail-service</module>
<module>detail-start</module>
</modules>
<!-- dependencyManagement非常重要,决定了子pom.xml是否可以直接引用父pom.xml的包 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--注意这个包就是项目本身的模块-->
<dependency>
<groupId>com.drawcode</groupId>
<artifactId>detail-service</artifactId>
<version>${project.version}</version>
<!-- 这个版本就表示0.0.1-SNAPSHOT -->
</dependency>
<!--注意这个包就是项目本身的模块-->
<dependency>
<groupId>com.drawcode</groupId>
<artifactId>detail-client</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 注意此处为空 -->
</plugins>
</build>
</project>
2、detail-start 的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--parent使用的即为父pom.xml的信息-->
<parent>
<groupId>com.drawcode</groupId>
<artifactId>detail-page</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>detail-start</artifactId>
<packaging>jar</packaging> <!-- 注意此处要配置为jar -->
<name>detail-start</name>
<!--子pom.xml不必添加dependencyManagement-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--这里可以看到因为父pom.xml已经引用了自身项目的包模块,所以这里可以不加version直接使用-->
<dependency>
<groupId>com.drawcode</groupId>
<artifactId>detail-service</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!--因为启动类在detail-start中,所以此处必须添加该plugin-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、detail-service 的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.drawcode</groupId>
<artifactId>detail-page</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>detail-service</artifactId>
<packaging>jar</packaging>
<name>detail-service</name>
<!--detail-service依赖于detail-client-->
<dependencies>
<dependency>
<groupId>com.drawcode</groupId>
<artifactId>detail-client</artifactId>
</dependency>
</dependencies>
</project>
4、detail-start 的 pom.xml
因为 detail-start 没有任何依赖所以比较简单
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.drawcode</groupId>
<artifactId>detail-page</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>detail-client</artifactId>
<packaging>jar</packaging>
<name>detail-client</name>
<dependencies>
</dependencies>
<build>
</build>
</project>
通过上述文件我们可以分析出以下关系:
- detail-page:父模块
- detail-client:子模块,无依赖
- detail-service:子模块,依赖detail-client
- detail-start:子模块,依赖detail-service
注意:在依赖引用过程中,千万不可以出现循环依赖,比如 client 引用了 service,service 也引用了 client,如果出现这种情况 maven 在打包的时候会直接报错
其中建议除了各个子模块单独使用的包之外,其他的都要在父模块下的 pom.xml 中配置包信息,这样便于包的版本控制
项目内部存在了包的依赖之后,不同模块之间的代码即可进行使用,比如 detail-service 依赖 detail-client,那么 detail-client 中的 Test2 就可以被 detail-service 使用了
但是反过来 detail-client 不可以使用 detail-service 中的类,因为依赖是单向的关系
如何启动
启动指令如下
$ mvn clean install && mvn spring-boot:run -pl detail-start
其中 spring-boot:run 可以使用就是因为 spring-boot-maven-plugin 的存在
-pl detail-start 则代表的是有 application 启动类的子模块目录
参考代码
https://github.com/guanpengchn/detail-page/tree/demo1
- 10招步骤保护IIS服务器安全
- Haproxy+Keepalived高可用环境部署梳理(主主和主从模式)
- Android-Universal-Image-Loader 图片异步加载类库的使用
- 工作组模式下专用队列(Private Queue)如何引用远程队列路径
- haproxy反向代理环境部署(http和https代理)
- 网站速度优化模块HttpCompressionModule
- mysql启动后随即关闭问题解决(ibdata1文件损坏导致)
- webvirtmgr-重命名kvm虚拟机的名称
- Lucene 2.0最基本的入门代码
- 网站每日PV/IP统计/总带宽/URL统计脚本分享(依据网站访问日志)
- 查看服务器系统资源(cpu,内容)利用率前几位的进程的方法
- 腾讯云平台部总经理陈磊:大数据背后的技术支撑
- DataSet与Xml相互转化
- SqlTransaction事务使用示例
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- CentOS 7 部署OpenLDAP+FreeRadius
- 手把手教你使用yolo进行对象检测
- K8s之Helm工具详解
- 技术创作101训练营——上古神器Gvim--从入门到精通
- 关于linux7下编写crontab任务执行mysqldump备份无效
- 黑暗中的YOLO:解决黑夜里的目标检测 | ECCV 2020
- Elasticsearch:Java 运用示例
- 【5分钟玩转Lighthouse】搭建个人云盘
- Elasticsearch:Index alias
- 编程神器来了!写代码、搜问题,全部都在「终端」完成!是时候入手了
- Array - 238. Product of Array Except Self
- Array - 209. Minimum Size Subarray Sum
- Array - 334. Increasing Triplet Subsequence
- Array - 123. Best Time to Buy and Sell Stock III
- Array - 122. Best Time to Buy and Sell Stock II