企业级依赖管理: 深入解读 Maven BOM

一、背景

当开发者在一个大型项目中使用 Maven 进行依赖管理时,项目往往会包含多个模块或子项目,并且这些模块会共享相同的依赖项。但是,不同模块可能会独立地指定各自的依赖版本,这可能导致以下问题:

  1. 依赖版本不一致: 不同模块中对相同依赖项使用不同的版本号,可能导致潜在的兼容性问题或冲突。
  2. 版本管理困难: 在多个模块中管理和维护依赖版本的一致性可能变得复杂,因为需要手动确保每个模块中的依赖版本保持同步。
  3. 重复配置: 在每个模块中单独指定依赖项的版本,导致了大量的重复配置,增加了维护成本。

为解决以上问题,Maven BOM(Bill of Materials,依赖关系管理)被引入,提供了一种集中管理依赖版本的方法。


此时有些同学可能会有疑问:我可以在项目的根pom中通过<dependencyManagement> 标签来集中化管理项目的所有依赖啊。

诚然,当项目使用 Maven 的 dependencyManagement 标签集中管理依赖时,确实能够集中指定依赖版本,但这种方式并不能将该项目的依赖版本供其他项目使用;相信大家对springboot并不陌生,在使用springboot的开发过程中我们通常可以看到如下依赖:

<!--引入Spring Boot官方维护的bom依赖清单-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.6.3</version><type>pom</type><scope>import</scope>
</dependency>
<!--引入阿里依赖bom清单-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>aliyun-spring-boot-dependencies</artifactId><version>1.0.0</version><type>pom</type><scope>import</scope>
</dependency

Spring Boot BOM(Bill of Materials,依赖关系管理)的优势在于,它不仅集中管理依赖版本,还允许其他项目通过引用 Spring Boot BOM 来继承其依赖版本管理的功能。换句话说,其他项目可以直接引入 Spring Boot BOM,继承其所管理的依赖版本,而无需单独指定每个依赖的版本。

相比之下,普通的 Maven 项目中,使用 dependencyManagement 标签虽然能够在当前项目中集中管理依赖版本,但其他项目无法直接继承该管理依赖版本的配置,需要手动复制相应的依赖管理部分到其他项目中,这样会增加维护成本且可能出现版本不一致的问题。

文章中提及的所有代码示例都可以在 GitHub 上找到:maven-bom-examples

二、什么是BOM

Maven 项目的打包类型时,通常可以分为三种:

  1. jar: 这是最常见的打包类型,适用于普通的 Java 项目。项目会以 jar 类型的格式进行打包。
  2. pom: 这种类型的项目通常被用作多模块项目的管理和维护。在父模块中定义了依赖项、插件等内容,实现了对版本的统一维护管理。
  3. war: 这类项目打包成为可运行的 Java Web 项目,例如可以在诸如 Tomcat、Jetty 等应用服务器上运行。

BOM 实际上是一个以 POM 类型定义的普通 Maven 项目,主要用于维护描述 Maven 项目所需的一系列公共依赖信息。通过引用 BOM 项目,可以实现对依赖版本的统一维护管理,而无需明确指定每个依赖项的版本号。


BOM 文件结构定义结构

<parent><groupId>org.example</groupId><artifactId>maven-bom-examples</artifactId><version>${revision}</version>
</parent>
<!--pom类型,多模块中依赖统一管理维护-->
<packaging>pom</packaging>
<artifactId>example-bom</artifactId><properties><!--统一维护管理变量-->
</properties><dependencyManagement><dependencies><!--统一维护管理的依赖--></dependencies>
</dependencyManagement><build><pluginManagement><plugins><!--插件依赖统一管理--></plugins></pluginManagement>
</build>

三、使用指南

3.1、创建bom

当创建 BOM(Bill Of Materials,物料清单)时,常见的两种方式包括:

  1. 单独仓库存放依赖: 在一个专门的仓库中,建立 BOM 模块用于存放特定需求的依赖信息。这种方法适用于需要隔离和管理独立的依赖需求,确保依赖版本独立于其他项目。
  2. 应用内创建 BOM 模块: 在现有应用的仓库中创建 BOM 模块,让应用的所有子模块共享使用。这种方法适用于在应用内统

选择使用哪种方式创建 BOM 取决于需求的特点和项目的架构,本文参考aliyun-spring-boot 实现方式,即应用内创建BOM模块。


新建一个maven-bom-examples项目,其目录结构如下:

maven-bom-examples
├─.idea
├─example-bom
│  └─pom.xml
└─pom.xml

example-bom模块pom.xml文件如下:

<parent><groupId>org.example</groupId><artifactId>maven-bom-examples</artifactId><version>${revision}</version>
</parent>
<!-- pom类型 -->
<packaging>pom</packaging>
<artifactId>example-bom</artifactId><properties><mysql.connector.java.version>8.0.33</mysql.connector.java.version><lingxi.cas.starter.version>1.1.4-RELEASE</lingxi.cas.starter.version><!-- 等等 -->
</properties><dependencyManagement><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.connector.java.version}</version></dependency><dependency><groupId>com.qihoo.finance.lingxi</groupId><artifactId>lingxi-cas-starter</artifactId><version>${lingxi.cas.starter.version}</version></dependency><!-- 等等 --></dependencies>
</dependencyManagement><build><pluginManagement><plugins><plugin><!-- 等等 --></plugin></plugins></pluginManagement>
</build></project>

3.2、错误使用:循环依赖

为了maven-bom-examples后续创建新的子模块可以使用bom的统一依赖,传统方式应该是将example-bom的GAV坐标放在maven-bom-examples<dependencyManagement>依赖标签中,从而作用于后续的子模块,pom如下:

<groupId>org.example</groupId>
<artifactId>maven-bom-examples</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<modules><module>example-bom</module>
</modules><properties><revision>1.0.0-SNAPSHOT</revision><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencyManagement><dependencies><dependency><groupId>org.example</groupId><artifactId>example-bom</artifactId><version>${revision}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement></project>

然而这种做法会导致项目打包报错,如下:循环依赖

> mvn clean install
[ERROR]   The project org.example:maven-bom-examples:1.0.0-SNAPSHOT (D:\IdeaProjects\maven-bom-examples\pom.xml) has 1 error
[ERROR]   The dependencies of type=pom and with scope=import form a cycle: org.example:example-bom:1.0.0-SNAPSHOT -> org.example:example-bom:1.0.0-SNAPSHOT @ org.example:example-bom:1.0.0-SNAPSHOT

导致循环依赖的原因在于:maven-bom-examples的POM 是以 type=pomscope=import 的方式导入example-bom的pom,而example-bom的pom又依赖于父pom,即maven-bom-examples,因而形成了循环依赖导致maven报错;需要注意的是如果依赖的是jar包类型的子模块则不会有循环依赖问题,

例如新增一个example-api子模块,此时目录结构如下:

maven-bom-examples
├─.idea
├─example-api
│  └─src
│  └─pom.xml
├─example-bom
│  └─pom.xml
└─pom.xml

maven-bom-examplespom新增example-api依赖,更改如下:

<dependencyManagement><dependencies>
<!--            <dependency>-->
<!--                <groupId>org.example</groupId>-->
<!--                <artifactId>example-bom</artifactId>-->
<!--                <version>${revision}</version>-->
<!--                <type>pom</type>-->
<!--                <scope>import</scope>-->
<!--            </dependency>--><dependency><groupId>org.example</groupId><artifactId>example-api</artifactId><version>${revision}</version></dependency></dependencies>
</dependencyManagement>

此时打包正常,可以看到example-apiexample-bom 处于同级目录,却只有example-bom 出现了循环依赖,这根本原因便在于<scope>import</scope>标签;以当前项目为例,当根pom引用了 <scope>import</scope> 的 BOM 时,而这个 BOM 又包含了指向根 POM 的依赖,就会导致循环依赖的问题。

example-api模块为普通jar包模块故不会导致循环依赖问题。

3.3、正确使用

正确的使用方式是创建一个名为 example-parent 模块,使其与 example-bom处于同级目录下,并在 example-parent 模块中导入 BOM 的依赖。然后,其他子模块只需引入 example-parent 模块作为其父模块,从而实现对 BOM 依赖的继承。此时目录结构如下:

maven-bom-examples
├─.idea
├─example-api
│  └─src
│  └─pom.xml
├─example-parent
│  └─pom.xml
├─example-bom
│  └─pom.xml
└─pom.xml

example-parent pom如下:由于example-parentexample-bom处于同级目录下,故不会出现循环依赖问题。

<parent><groupId>org.example</groupId><artifactId>maven-bom-examples</artifactId><version>${revision}</version>
</parent>
<packaging>pom</packaging>
<artifactId>example-parent</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencyManagement><dependencies><dependency><groupId>org.example</groupId><artifactId>example-bom</artifactId><version>${revision}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement></project>

maven-bom-examplespom:无依赖

<groupId>org.example</groupId>
<artifactId>maven-bom-examples</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<modules><module>example-bom</module><module>example-api</module><module>example-parent</module>
</modules><properties><revision>1.0.0-SNAPSHOT</revision><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties></project>

3.4、创建子模块

由于 example-parent 模块引入了 BOM 依赖,因此,若要在后续的子模块中使用统一的依赖管理,只需要在 example-parent 父模块下创建相应的子模块即可实现统一依赖的继承。例如新建example-model,此时目录结构如下:

maven-bom-examples
├─.idea
├─example-api
│  ├─src
│  └─pom.xml
├─example-parent  
│  ├─example-model
│  │  ├─src
│  │  └─pom.xml
│  └─pom.xml
├─example-bom
│  └─pom.xml
└─pom.xml

example-parentpom如下:

<parent><groupId>org.example</groupId><artifactId>maven-bom-examples</artifactId><version>${revision}</version>
</parent>
<packaging>pom</packaging>
<artifactId>example-parent</artifactId><modules><!-- 新增子模块 --><module>example-model</module>
</modules><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencyManagement><dependencies><dependency><groupId>org.example</groupId><artifactId>example-bom</artifactId><version>${revision}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

example-modelpom如下:

<parent><groupId>org.example</groupId><artifactId>example-parent</artifactId><version>${revision}</version>
</parent><artifactId>example-model</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies><!-- 子模块使用了bom依赖而无需指定version --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.qihoo.finance.lingxi</groupId><artifactId>lingxi-cas-starter</artifactId></dependency>
</dependencies>

3.5、扩展:<relativePath>

<relativePath> 元素是 Maven POM 文件中 <parent> 元素的一个子元素,用于指定父模块相对于当前子模块的路径,它告诉 Maven 在哪里找到父 POM,如下:

<parent><groupId>xxx</groupId><artifactId>xxx</artifactId><version>xxx</version><relativePath>../xxx/pom.xml</relativePath>
</parent>

这个元素通常在子模块的 POM 文件中用于指定父 POM 文件的路径,以便在项目中更灵活地管理父子模块之间的关系。

如果父模块就在当前项目的根目录下,则不需要指定 <relativePath> 元素,例如上面的example-model子模块就无需使用<relativePath> 标签。

之所以要介绍<relativePath> 标签是因为有时我们为了更直观的查看所有应用模块而将所有子模块全部放在根目录下,例如开源项目debezium:

在这里插入图片描述

此时新增example-relative子模块,目录结构如下:

maven-bom-examples
├─.idea
├─example-api
│  ├─src
│  └─pom.xml
├─example-parent  
│  ├─example-model
│  │  ├─src
│  │  └─pom.xml
│  └─pom.xml
├─example-bom
│  └─pom.xml
├─example-relative
│  ├─src
│  └─pom.xml
└─pom.xml

example-relativepom:

<parent><groupId>org.example</groupId><artifactId>example-parent</artifactId><version>${revision}</version><!-- 由于与example-parent同级目录, 使用relativePath标签指定父POM路径 --><relativePath>../example-parent/pom.xml</relativePath>
</parent><artifactId>example-relative</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies><!-- 子模块无需指定version, 而是使用了bom依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
</dependencies>

example-parentpom:

<parent><groupId>org.example</groupId><artifactId>maven-bom-examples</artifactId><version>${revision}</version>
</parent>
<packaging>pom</packaging>
<artifactId>example-parent</artifactId><modules><module>example-model</module><!-- 新增子模块 --><module>../example-relative</module>
</modules><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencyManagement><dependencies><dependency><groupId>org.example</groupId><artifactId>example-bom</artifactId><version>${revision}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

四、总结

当涉及到大型项目和多模块应用时,Maven BOM(Bill of Materials)提供了一种管理依赖版本的强大机制。通过创建BOM可以:

  • 集中管理依赖的版本号,确保各模块使用相同的依赖版本。
  • 减少版本冲突和不一致性的问题,提高项目的稳定性和可靠性。
  • 允许其他项目引用BOM并继承其依赖版本,减少重复配置的需求,提高项目的可维护性和一致性。

深入理解和有效使用Maven BOM有助于简化依赖管理过程,提高项目的开发效率和整体质量。

五、相关资料

  • debezium
  • aliyun-spring-boot-parent

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2660818.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙APP的代码规范

鸿蒙APP的代码规范是为了确保代码质量、可读性和可维护性而定义的一系列规则和标准。以下是一些建议的鸿蒙APP代码规范&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 代码风格&#xff1a; 采用…

【完整思路】2023 年中国高校大数据挑战赛 赛题 B DNA 存储中的序列聚类与比对

2023 年中国高校大数据挑战赛 赛题 B DNA 存储中的序列聚类与比对 任务 1.错误率和拷贝数分析&#xff1a;分析“train_reads.txt”和“train_reference.txt”数据集中的错误率&#xff08;插入、删除、替换、链断裂&#xff09;和序列拷贝数。 2.聚类模型开发&#xff1a;开发…

WPF Button使用漂亮 控件模板ControlTemplate 按钮使用控制模板实例及源代码 设计一个具有圆角边框、鼠标悬停时颜色变化的按钮模板

续前两篇模板文章 模板介绍1 模板介绍2 WPF中的Button控件默认样式简洁&#xff0c;但可以通过设置模板来实现更丰富的视觉效果和交互体验。按钮模板主要包括背景、边框、内容&#xff08;通常为文本或图像&#xff09;等元素。通过自定义模板&#xff0c;我们可以改…

GoogleNetv1:Going deeper with convolutions更深的卷积神经网络

文章目录 GoogleNetv1全文翻译论文结构摘要1 引言2 相关工作3 动机和高层考虑稀疏矩阵 4 结构细节引入1x1卷积核可以减少通道数 5 GoogleNet6 训练方法7 ILSVRC 2014 分类挑战赛设置和结果8 ILSVRC 2014检测挑战赛设置和结果9 总结 论文研究背景、成果及意义论文图表 GoogleNet…

锐捷路由小型综合实验

一、实验拓扑 二、实验目的 1、熟练掌握ospf的配置 2、熟练掌握RIP的配置 3、熟练掌握静态路由的配置 4、熟练掌握各种路由协议之间的引入 5、熟练掌握telnet和ssh的配置 三、实验配置 R1 //配置telent username admin password admin123 enable password admin123 enable…

鸿蒙原生应用再添新丁!搜狐集团、航旅纵横入局鸿蒙

鸿蒙原生应用再添新丁&#xff01;搜狐集团、航旅纵横入局鸿蒙 来自 HarmonyOS 微博12月28日消息&#xff0c;搜狐集团宣布与华为达成全面合作&#xff01;搜狐新闻近期将完成#鸿蒙原生应用#核心功能版本&#xff0c;搜狐视频也启动了#鸿蒙原生应用#开发&#xff01;这不仅是一…

处理HTTP错误响应:Go语言中的稳健之道

开场白&#xff1a;在Web开发中&#xff0c;HTTP错误响应是不可避免的一部分。当请求无法成功完成时&#xff0c;服务器会返回一个错误响应。今天&#xff0c;我们将深入探讨如何在Go语言中优雅地处理这些HTTP错误响应。 知识点一&#xff1a;HTTP错误响应的常见类型HTTP错误响…

阿里云数据库PolarDB费用价格_MySQL版_PolarDB_分布式版

阿里云数据库PolarDB租用价格表&#xff0c;云数据库PolarDB MySQL版2核4GB&#xff08;通用&#xff09;、2个节点、60 GB存储空间55元5天&#xff0c;云数据库 PolarDB 分布式版标准版2核16G&#xff08;通用&#xff09;57.6元3天&#xff0c;阿里云百科aliyunbaike.com分享…

SQL Server 存储过程 触发器 事务处理

CSDN 成就一亿技术人&#xff01; 难度指数&#xff1a;* * CSDN 成就一亿技术人&#xff01; 目录 1. 存储过程的作用 创建存储过程 2. 触发器 触发器的种类 insert触发器 update触发器 delete触发器 测试 3. 事务 开始事务 提交事务 回滚事务 举个实例 在 SQ…

gitlab请求合并分支

直接去看原文: 原文链接:Gitlab合并请求相关流程_source branch target branch-CSDN博客 --------------------------------------------------------------------------------------------------------------------------------- 入口&#xff1a; 仓库控制台的这两个地方都…

第4章 信息收集

本章将阐述信息收集的概念及其作用&#xff0c;进而介绍信息收集阶段的各项渗透测试工作。此外&#xff0c;我们还会介绍 Kali Linux 收录的信息收集工具。希望读者在阅读本章之后能够理解在信息收集阶段的渗透测试工作&#xff0c;并且能够在实际的渗透测试中顺利收集各种必要…

javaWebssh民宿管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh民宿管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模 式开发。开发环境为TOMCAT7.0,My…

Spark编程范例:Word Count示例解析

Apache Spark是一个强大的分布式计算框架&#xff0c;用于处理大规模数据。Word Count示例是Spark入门教程中的经典示例&#xff0c;旨在展示如何使用Spark来进行简单的文本处理和数据分析。本文将深入解析Word Count示例&#xff0c;以帮助大家更好地理解Spark的基本概念和编程…

V-rep(CoppeliaSim)添加相机,与python联合仿真,并使用python读取V-rep中的RGB图与深度图

目录 前言在V-rep中构建场景建立python与V-rep通信 前言 本文主要介绍了如何使用python与V-rep联合仿真&#xff0c;并用OpenCV可视化V-rep中视觉传感器所能看到的 RGB图和深度图&#xff0c;效果图如下。 在V-rep中构建场景 本文使用的V-rep版本是3.5&#xff1a; 打开V-…

再谈动态SQL

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 文章目录 专栏精选摘要引言正文动态sql标签ifchoose...when...otherwisewhere、…

如何实现WinApp的UI自动化测试?

WinApp&#xff08;WindowsAPP&#xff09;是运行在Windows操作系统上的应用程序&#xff0c;通常会提供一个可视的界面&#xff0c;用于和用户交互。例如运行在Windows系统上的Microsoft Office、PyCharm、Visual Studio Code、Chrome&#xff0c;都属于WinApp。常见的WinApp&…

日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队

前言 随着互联网和大数据的迅猛发展&#xff0c;分布式日志系统和日志分析系统已广泛应用&#xff0c;几乎所有应用程序都使用各种日志框架记录程序运行信息。因此&#xff0c;作为工程师&#xff0c;了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影…

使用spring boot实现异常的统一返回

在这个前后端分离的时代&#xff0c;一个 统一的数据格式非常重要。本次我们实现用spring boot实现一下返回给前端数据的统一格式&#xff0c;不再出现服务器500的错误。 新建一个spring boot项目&#xff0c;并导入knife4j的依赖。 写一个controller控制器&#xff0c;用来是…

maven中dependencyManagement标签

简介 dependencyManagement正如其名&#xff0c;用于项目依赖的统一管理。 在父项目中的pom.xml文件中加入dependencyManagement标签即可完成依赖版本的声明。在声明完成后&#xff0c;子项目&#xff08;module&#xff09;中引用相同的依赖时可以不指定version标签自动引入…

<软考高项备考>《论文专题 - 33 沟通管理(1) 》

1 成本管理基础 1.1 写作要点 过程定义、作用写作要点、思路规划沟通管理规划沟通管理是基于每个干系人或干系人群体的信息需求、可用的组织资产&#xff0c;以及具体项目的需求&#xff0c;为项目沟通活动制定恰当的方法和计划的过程。作用:①及时向干系人提供相关信息;②引…