大家好,我是G探险者!
最近项目上在整理各个组件的POM依赖,试图通过一个父级POM来管理整个项目上使用到的各种开源的,和自研的依赖。目的是为了规范使用我们框架的应用,不私自在自身项目里面乱定版本,导致各种版本冲突不兼容的问题。
在这个整理的过程中我们也遇到了一些坑,比如有的组件里面使用了netty,中的某个依赖包,另外的组件使用到了netty的另外的依赖包,那么我们在统一管理netty这些依赖的时候,维护了的netty的依赖版本就有点五花八门,导致应用在使用我们的这个父级POM时,出现了一些奇奇怪怪的版本不兼容问题。
于是引入了BOM这个概念,今天我们就来聊一聊BOM是如何进行多模块项目的依赖管理的。
BOM 的由来
BOM(Bill of Materials,材料清单)最早的概念来源于制造业,用于描述构建产品所需的组件清单。在软件开发领域,BOM 被用于列出某个项目或工具的所有依赖及其版本信息,确保这些依赖模块能够兼容协作。
在 Java 的依赖管理工具中,如 Maven 和 Gradle,BOM 解决了模块化项目中版本管理的复杂性问题。许多大型项目(如 Spring Framework、Jetty、netty,springcloud)随着功能的扩展和模块化设计,逐步拆分为多个子模块。每个子模块可能独立演进,但它们之间需要确保版本一致和兼容性。于是引入了 BOM 来统一管理这些模块的版本。
BOM 的作用
-
统一版本管理
- BOM 是一个特殊的 Maven POM 文件(
pom.xml
),它列出了多个相关模块及其推荐的版本。 - 使用 BOM 后,开发者无需在每个依赖声明中显式指定版本,避免版本冲突和错误。
示例:
<dependencyManagement> <dependencies> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>11.0.15</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> <version>11.0.15</version> </dependency> <!-- 其他模块省略 --> </dependencies> </dependencyManagement>
- BOM 是一个特殊的 Maven POM 文件(
-
避免版本冲突
- 在大型项目中,不同模块可能依赖同一个库的不同版本。BOM 确保所有模块使用相同版本,从而避免潜在冲突。
-
简化依赖声明
- 在 Maven 中,通过引入 BOM 后,子模块的版本可以被自动解析。
示例:
<dependencyManagement> <dependencies> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-bom</artifactId> <version>11.0.15</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
子模块中使用依赖时不再需要显式声明版本:
<dependencies> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> </dependency> </dependencies>
-
提高维护效率
- BOM 集中管理所有模块的版本,可以在 BOM 文件中一次性升级所有相关依赖版本,降低手动管理的复杂性。
-
明确兼容性
- BOM 通常由项目开发团队发布,确保其中列出的模块版本是经过测试、互相兼容的。
BOM 的使用场景
-
多模块项目
- 适用于一个大项目分拆成多个模块(如 Jetty),开发团队可以提供 BOM 供用户引用,避免用户在不同模块间手动匹配版本。
-
集成框架
- 像 Spring Boot 提供了自己的 BOM,管理 Spring 框架及常用第三方库的版本。开发者只需选择一个 BOM 文件即可。
Spring Boot 示例:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
-
企业级项目
- 企业内部开发的框架或工具库,可以用 BOM 提供统一的依赖管理,提升团队协作效率。
家族式依赖和非家族式依赖
非家族式依赖顾名思义,就是它没有过多的相关联的依赖包,在维护时只需要一个依赖即可,比如mysql的驱动包
所谓家族式依赖就是指的是,像jetty,netty,springcloud等这样的依赖,比如jetty家族:
依赖包 | 功能描述 | 依赖模块 |
---|---|---|
核心组件 | ||
jetty-util | 提供通用工具库(线程池、集合工具、日志等)。 | 无直接依赖 |
jetty-io | 异步 IO 操作支持,底层 IO 模块。 | jetty-util |
jetty-http | 处理 HTTP 协议解析和生成。 | jetty-io |
jetty-server | Jetty 核心服务器模块,处理 HTTP 请求与响应。 | jetty-http , jetty-io , jetty-util |
jetty-servlet | 提供 Servlet 容器支持。 | jetty-server |
高级功能模块 | ||
jetty-security | 提供认证和授权功能。 | jetty-server , jetty-util |
jetty-proxy | 实现反向代理功能。 | jetty-server |
jetty-websocket-server | WebSocket 服务端支持。 | jetty-server , jetty-websocket-common |
jetty-websocket-client | WebSocket 客户端支持。 | jetty-client , jetty-websocket-common |
集成模块 | ||
jetty-servlets | 提供扩展的 servlet 功能和过滤器支持。 | jetty-servlet |
jetty-jndi | 提供 JNDI 支持。 | jetty-server , jetty-util |
jetty-alpn | 支持 HTTP/2 中的 ALPN 协议。 | jetty-server , jetty-io |
测试与开发工具 | ||
jetty-annotations | 支持基于注解的配置(如 Servlet 3.0 注解)。 | jetty-servlet |
jetty-test-helper | 提供测试工具,便于编写测试用例。 | jetty-server |
jetty-distribution | 提供完整的 Jetty 服务器分发包,适合快速运行与调试。 | 包含多个核心模块 |
netty家族:
模块 | artifactId | 功能描述 |
---|---|---|
Netty BOM | netty-bom | 包含所有 Netty 相关模块的版本定义,用于依赖版本管理 |
Netty Common | netty-common | 提供 Netty 的常用工具类,例如线程池、日志、时间处理等 |
Netty Transport | netty-transport | 提供高性能的网络传输功能,包括 NIO、EPOLL、KQueue 等支持 |
Netty Codec | netty-codec | 提供对各种协议的编码解码支持(如 HTTP、HTTP/2、WebSocket 等) |
Netty Handler | netty-handler | 提供常见的 I/O 事件处理程序(例如 HTTP 请求处理、SSL/TLS 加密、WebSocket 等) |
Netty Buffer | netty-buffer | 提供 Netty 的内存缓冲区 API |
Netty Codecs HTTP | netty-codec-http | 提供 HTTP 协议的编码解码器支持 |
Netty Codecs SSL | netty-codec-ssl | 提供 SSL/TLS 编码解码支持 |
Netty Resolver | netty-resolver | 提供 DNS 解析、服务发现等功能 |
Netty Handler Proxy | netty-handler-proxy | 提供代理协议(如 HTTP 代理)的支持 |
Netty Handler SSL | netty-handler-ssl | 提供 SSL/TLS 协议的处理程序 |
Netty Transport Epoll | netty-transport-epoll | 支持基于 EPOLL 的传输机制(Linux 环境) |
Netty Transport KQueue | netty-transport-kqueue | 支持基于 KQueue 的传输机制(macOS 环境) |
这些家族式的依赖如果维护到一个父级pom里,应该如何更好的维护到一个POM里面呢,这就要发挥BOM的作用了。
以下我提供一个示例:
<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.example</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<!-- 家族依赖的版本管理 -->
<spring.boot.version>2.6.3</spring.boot.version>
<spring.cloud.version>2021.0.0</spring.cloud.version>
<netty.version>4.1.72.Final</netty.version>
<jetty.version>11.0.1</jetty.version>
<hutool.version>5.8.5</hutool.version>
<!-- 非家族依赖的版本管理 -->
<mysql.version>8.0.27</mysql.version>
<commons.lang.version>3.12.0</commons.lang.version>
<log4j.version>2.17.0</log4j.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 引入 Spring Boot BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入 Spring Cloud BOM -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入 Netty BOM -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>${netty.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入 Jetty BOM -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-bom</artifactId>
<version>${jetty.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 其他家族性质依赖管理 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<!-- 非家族依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
-
非家族依赖的版本管理:在
<dependencyManagement>
中加入了mysql-connector-java
、commons-lang3
和log4j-api
这些非家族性质的依赖。这样可以确保所有模块使用一致的版本,无需每个子模块都指定版本。 -
BOM 和常规依赖统一管理:现在无论是家族依赖(如 Spring Boot、Spring Cloud 等),还是常规依赖(如 MySQL、Commons、Log4j 等),都在
dependencyManagement
中进行了版本管理。 -
集中版本管理:所有的版本号都通过
${property.name}
统一管理,保证了依赖的版本一致性,方便在不同模块间共享和更新版本。
以上这种方式不仅集中管理了所有依赖的版本,还确保了无论是家族性质的依赖,还是其他常规依赖,都能在一个地方进行统一的版本控制。