如果说 Spring Boot
解决了 Java
工程师 Web
开发的痛点,那 Maven
则是当之无愧的 Java
依赖管理大师。回顾没有 Maven
的日子,手动下载管理工程依赖的各个 jar
包可谓是每个开发者的噩梦。
时至今日,Maven
的社区生态已然蓬勃发展,不仅提供了快速的依赖管理,同时对项目的模块化管理也提供了优秀的设计思路。
今天,文章的核心重点将侧重于如何通过 Maven
合理的管理项目结构,实现的工程的整洁性。
1. 工程结构
以流行的前端后分离项目为例,在具体展开之前先看一下完整的项目模块结构,如下图所示:
想要一个合理的项目结构,最重要的就是将功能进行拆分,而不是一股脑塞进单个模块。以上图的示例,其同样也可以合并为两个子模块:前端Web
与后端 Server
,但后端模块也随之变得臃肿不堪。
2. 模块拆分
在上述提到了应对工程进行模块拆分,但也需注意一点,并不是一定说模块合并就是不好,而是应根据工程的规模进行抉择。
举个例子,需要开发系统仅包含一两个页面数据简单展示,此时完全可以单个工程模块一把梭哈。而若需要开发一个复杂的系统,包含用户权限以及多个功能的业务逻辑处理,此时宏观上根据系统功能拆分为单独的子模块显然更为合理。
以前文图示为例,将一个系统分为四个大类:权限管理 (basic)
、业务处理 (logic)
、对外服务 (protocol)
以及前端页面 (web)
。每一个子模块仅负责单独业务功能,同时模块拆分也带来另一项额外收益,以权限管理模块为例,当其它项目同样涉及到此类需求时,可通过 Maven
依赖实现代码复用。倘若未执行模块拆分想要实现复用,采用整个完成完整引用的方式将会引入大量无用功能从而无端膨胀项目体积,另一种方式则需要手动继续代码剥离从而拷贝到新项目,二者都并非最优解。
3. 依赖管理
通过模块的拆分实现了工程的规整性,而系统的依赖则通过 dependencyManagement
实现统一管理。
所谓统一依赖管理,核心即在于对依赖库的版本管理,目的是实现单一入口的版本管理,更通俗的讲即版本集中统一定义,一旦定义则全局生效。
(1) bom模块
在 Maven
中规定俗成即 bom
模块通过 dependencyManagement
实现依赖库版本的统一管理。
在之前介绍 Maven
的文章中已经详细介绍了 dependencyManagement
的作用,即定义依赖版本作用域等信息,子模块通过继承或导入该 bom 模块后,使用依赖时通过 groupId
与 artifactId
标签即可。
详细的 Maven
使用教程可参考以往文章:史上最全Maven教程,没有之一。
下述则是一个 bom
模块的定义示例,需要注意模块的 packaging
需设置为 pom
而非 jar
,因其为依赖定义管理而不存在具体的依赖引用。
<?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>
<parent>
<groupId>xyz.ibudai</groupId>
<artifactId>blank-web-template</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<artifactId>template-bom</artifactId>
<packaging>pom</packaging>
<properties>
<commons-lang3.version>3.12.0</commons-lang3.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
</dependencyManagement>
</project>
(2) parent模块
完成 bom
模块的定义之后,则定义工程的父模块,核心即将上述定义的 bom 模块进行导入,而后其它子模块继承此 parent
模块即可读取 bom
中定义依赖版本信息。
值得一提的是 bom
模块的 dependencyManagement
不仅可通过下述示例的 import
方式导入,同时也可通过 <parent>
标签从而直接继承获取,只是在 Spring Boot
项目中通常 parent
习惯继承于 springframework
因此采用导入方式引入 bom
模块。
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>xyz.ibudai</groupId>
<artifactId>template-parent</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>xyz.ibudai</groupId>
<artifactId>template-bom</artifactId>
<version>${project.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
</project>
(3) 业务模块
针对其它拆分的子模块而言,只需继承上述定义的 parent
模块即可。
在继承定义的 parent
模块后同理会继承其 dependencyManagement
信息,因此在模块中引入依赖时仅需 groupId
与 artifactId
标签引用即可,无需额外指定版本信息其将会读取 bom
模块中的信息。
通过此种方式,即实现了工程依赖的统一管理,所有涉及的依赖库其版本与作用域等信息统一定义入口于 bom
模块,当需要进行版本升级等操作时,只需修改 bom
定义的信息即可全局生效。
需要注意的一点是此处 <parent>
中指定了 <relativePath>
是因为 Maven
默认的父模块寻址逻辑为当前模块的上一级,若不存在则读取仓库信息。在之前的图示中可以看到,子模块 basic
与 logic
等本应建于 parent
模块下,但为了结构统一将其创建于同级目录下,因此这里手动通过 relativePath 指定父模块配置路径。
<?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>
<parent>
<groupId>xyz.ibudai</groupId>
<artifactId>template-parent</artifactId>
<version>0.1-SNAPSHOT</version>
<relativePath>../template-parent/pom.xml</relativePath>
</parent>
<artifactId>template-logic</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
示例项目:GitHub直达