电商网站的需求文档,简单的asp网站,seo前线,今晚12点上海又要封控了吗一、背景
当开发者在一个大型项目中使用 Maven 进行依赖管理时#xff0c;项目往往会包含多个模块或子项目#xff0c;并且这些模块会共享相同的依赖项。但是#xff0c;不同模块可能会独立地指定各自的依赖版本#xff0c;这可能导致以下问题#xff1a;
依赖版本不一致…一、背景
当开发者在一个大型项目中使用 Maven 进行依赖管理时项目往往会包含多个模块或子项目并且这些模块会共享相同的依赖项。但是不同模块可能会独立地指定各自的依赖版本这可能导致以下问题
依赖版本不一致 不同模块中对相同依赖项使用不同的版本号可能导致潜在的兼容性问题或冲突。版本管理困难 在多个模块中管理和维护依赖版本的一致性可能变得复杂因为需要手动确保每个模块中的依赖版本保持同步。重复配置 在每个模块中单独指定依赖项的版本导致了大量的重复配置增加了维护成本。
为解决以上问题Maven BOMBill of Materials依赖关系管理被引入提供了一种集中管理依赖版本的方法。 此时有些同学可能会有疑问我可以在项目的根pom中通过dependencyManagement 标签来集中化管理项目的所有依赖啊。
诚然当项目使用 Maven 的 dependencyManagement 标签集中管理依赖时确实能够集中指定依赖版本但这种方式并不能将该项目的依赖版本供其他项目使用相信大家对springboot并不陌生在使用springboot的开发过程中我们通常可以看到如下依赖
!--引入Spring Boot官方维护的bom依赖清单--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-dependencies/artifactIdversion2.6.3/versiontypepom/typescopeimport/scope
/dependency
!--引入阿里依赖bom清单--
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdaliyun-spring-boot-dependencies/artifactIdversion1.0.0/versiontypepom/typescopeimport/scope
/dependencySpring Boot BOMBill of Materials依赖关系管理的优势在于它不仅集中管理依赖版本还允许其他项目通过引用 Spring Boot BOM 来继承其依赖版本管理的功能。换句话说其他项目可以直接引入 Spring Boot BOM继承其所管理的依赖版本而无需单独指定每个依赖的版本。
相比之下普通的 Maven 项目中使用 dependencyManagement 标签虽然能够在当前项目中集中管理依赖版本但其他项目无法直接继承该管理依赖版本的配置需要手动复制相应的依赖管理部分到其他项目中这样会增加维护成本且可能出现版本不一致的问题。
文章中提及的所有代码示例都可以在 GitHub 上找到maven-bom-examples
二、什么是BOM
Maven 项目的打包类型时通常可以分为三种
jar 这是最常见的打包类型适用于普通的 Java 项目。项目会以 jar 类型的格式进行打包。pom 这种类型的项目通常被用作多模块项目的管理和维护。在父模块中定义了依赖项、插件等内容实现了对版本的统一维护管理。war 这类项目打包成为可运行的 Java Web 项目例如可以在诸如 Tomcat、Jetty 等应用服务器上运行。
BOM 实际上是一个以 POM 类型定义的普通 Maven 项目主要用于维护描述 Maven 项目所需的一系列公共依赖信息。通过引用 BOM 项目可以实现对依赖版本的统一维护管理而无需明确指定每个依赖项的版本号。 BOM 文件结构定义结构
parentgroupIdorg.example/groupIdartifactIdmaven-bom-examples/artifactIdversion${revision}/version
/parent
!--pom类型多模块中依赖统一管理维护--
packagingpom/packaging
artifactIdexample-bom/artifactIdproperties!--统一维护管理变量--
/propertiesdependencyManagementdependencies!--统一维护管理的依赖--/dependencies
/dependencyManagementbuildpluginManagementplugins!--插件依赖统一管理--/plugins/pluginManagement
/build三、使用指南
3.1、创建bom
当创建 BOMBill Of Materials物料清单时常见的两种方式包括
单独仓库存放依赖 在一个专门的仓库中建立 BOM 模块用于存放特定需求的依赖信息。这种方法适用于需要隔离和管理独立的依赖需求确保依赖版本独立于其他项目。应用内创建 BOM 模块 在现有应用的仓库中创建 BOM 模块让应用的所有子模块共享使用。这种方法适用于在应用内统
选择使用哪种方式创建 BOM 取决于需求的特点和项目的架构本文参考aliyun-spring-boot 实现方式即应用内创建BOM模块。 新建一个maven-bom-examples项目其目录结构如下
maven-bom-examples
├─.idea
├─example-bom
│ └─pom.xml
└─pom.xmlexample-bom模块pom.xml文件如下
parentgroupIdorg.example/groupIdartifactIdmaven-bom-examples/artifactIdversion${revision}/version
/parent
!-- pom类型 --
packagingpom/packaging
artifactIdexample-bom/artifactIdpropertiesmysql.connector.java.version8.0.33/mysql.connector.java.versionlingxi.cas.starter.version1.1.4-RELEASE/lingxi.cas.starter.version!-- 等等 --
/propertiesdependencyManagementdependenciesdependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion${mysql.connector.java.version}/version/dependencydependencygroupIdcom.qihoo.finance.lingxi/groupIdartifactIdlingxi-cas-starter/artifactIdversion${lingxi.cas.starter.version}/version/dependency!-- 等等 --/dependencies
/dependencyManagementbuildpluginManagementpluginsplugin!-- 等等 --/plugin/plugins/pluginManagement
/build/project3.2、错误使用循环依赖
为了maven-bom-examples后续创建新的子模块可以使用bom的统一依赖传统方式应该是将example-bom的GAV坐标放在maven-bom-examples的dependencyManagement依赖标签中从而作用于后续的子模块pom如下
groupIdorg.example/groupId
artifactIdmaven-bom-examples/artifactId
version${revision}/version
packagingpom/packaging
modulesmoduleexample-bom/module
/modulespropertiesrevision1.0.0-SNAPSHOT/revisionmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/propertiesdependencyManagementdependenciesdependencygroupIdorg.example/groupIdartifactIdexample-bom/artifactIdversion${revision}/versiontypepom/typescopeimport/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 typepom and with scopeimport 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 是以 typepom 和 scopeimport 的方式导入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.xmlmaven-bom-examplespom新增example-api依赖更改如下
dependencyManagementdependencies
!-- dependency--
!-- groupIdorg.example/groupId--
!-- artifactIdexample-bom/artifactId--
!-- version${revision}/version--
!-- typepom/type--
!-- scopeimport/scope--
!-- /dependency--dependencygroupIdorg.example/groupIdartifactIdexample-api/artifactIdversion${revision}/version/dependency/dependencies
/dependencyManagement此时打包正常可以看到example-api与example-bom 处于同级目录却只有example-bom 出现了循环依赖这根本原因便在于scopeimport/scope标签以当前项目为例当根pom引用了 scopeimport/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.xmlexample-parent pom如下由于example-parent 与 example-bom处于同级目录下故不会出现循环依赖问题。
parentgroupIdorg.example/groupIdartifactIdmaven-bom-examples/artifactIdversion${revision}/version
/parent
packagingpom/packaging
artifactIdexample-parent/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/propertiesdependencyManagementdependenciesdependencygroupIdorg.example/groupIdartifactIdexample-bom/artifactIdversion${revision}/versiontypepom/typescopeimport/scope/dependency/dependencies
/dependencyManagement/projectmaven-bom-examplespom无依赖
groupIdorg.example/groupId
artifactIdmaven-bom-examples/artifactId
version${revision}/version
packagingpom/packaging
modulesmoduleexample-bom/modulemoduleexample-api/modulemoduleexample-parent/module
/modulespropertiesrevision1.0.0-SNAPSHOT/revisionmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/properties/project3.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.xmlexample-parentpom如下
parentgroupIdorg.example/groupIdartifactIdmaven-bom-examples/artifactIdversion${revision}/version
/parent
packagingpom/packaging
artifactIdexample-parent/artifactIdmodules!-- 新增子模块 --moduleexample-model/module
/modulespropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/propertiesdependencyManagementdependenciesdependencygroupIdorg.example/groupIdartifactIdexample-bom/artifactIdversion${revision}/versiontypepom/typescopeimport/scope/dependency/dependencies
/dependencyManagementexample-modelpom如下
parentgroupIdorg.example/groupIdartifactIdexample-parent/artifactIdversion${revision}/version
/parentartifactIdexample-model/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/propertiesdependencies!-- 子模块使用了bom依赖而无需指定version --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdcom.qihoo.finance.lingxi/groupIdartifactIdlingxi-cas-starter/artifactId/dependency
/dependencies3.5、扩展relativePath
relativePath 元素是 Maven POM 文件中 parent 元素的一个子元素用于指定父模块相对于当前子模块的路径它告诉 Maven 在哪里找到父 POM如下
parentgroupIdxxx/groupIdartifactIdxxx/artifactIdversionxxx/versionrelativePath../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.xmlexample-relativepom
parentgroupIdorg.example/groupIdartifactIdexample-parent/artifactIdversion${revision}/version!-- 由于与example-parent同级目录, 使用relativePath标签指定父POM路径 --relativePath../example-parent/pom.xml/relativePath
/parentartifactIdexample-relative/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/propertiesdependencies!-- 子模块无需指定version, 而是使用了bom依赖 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependency
/dependenciesexample-parentpom
parentgroupIdorg.example/groupIdartifactIdmaven-bom-examples/artifactIdversion${revision}/version
/parent
packagingpom/packaging
artifactIdexample-parent/artifactIdmodulesmoduleexample-model/module!-- 新增子模块 --module../example-relative/module
/modulespropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/propertiesdependencyManagementdependenciesdependencygroupIdorg.example/groupIdartifactIdexample-bom/artifactIdversion${revision}/versiontypepom/typescopeimport/scope/dependency/dependencies
/dependencyManagement四、总结
当涉及到大型项目和多模块应用时Maven BOMBill of Materials提供了一种管理依赖版本的强大机制。通过创建BOM可以
集中管理依赖的版本号确保各模块使用相同的依赖版本。减少版本冲突和不一致性的问题提高项目的稳定性和可靠性。允许其他项目引用BOM并继承其依赖版本减少重复配置的需求提高项目的可维护性和一致性。
深入理解和有效使用Maven BOM有助于简化依赖管理过程提高项目的开发效率和整体质量。
五、相关资料
debeziumaliyun-spring-boot-parent