告别“复制粘贴”!微服务架构下如何统一管理POM依赖版本(实战详解)
一.先说一个场景?
很多从单体应用转型微服务的同学都会遇到这样一个尴尬的场景:
刚开始拆分单体项目时,每新建一个微服务(比如把单体项目的菜单功能单独拆成
menu-service微服务),都要去原来的单体项目里翻pom.xml,把Spring Boot、MyBatis、MySQL等依赖的版本号一个个复制过来。不仅麻烦,而且一旦要升级版本(比如Spring Boot从2.7升到3.0),你得去几十个微服务里逐个修改版本号,简直是“灾难现场”。
其实,成熟的微服务架构早就解决了这个问题。今天我们就来聊聊,如何通过“统一父工程 + BOM模式”,彻底解放双手,实现依赖版本的“中央集权”
思考:什么是BOM?
BOM 是Bill of Materials(物料清单)的缩写,在微服务架构中,它指代一份特殊的 Maven POM 文件。这份文件充当了依赖版本的“中央集权”角色,通过
<dependencyManagement>集中定义了所有第三方库的统一版本号。引入 BOM 后,各个微服务模块在声明依赖时无需再指定版本号,从而确保了整个系统依赖版本的一致性,有效避免了版本冲突,并极大地简化了后续的升级与维护工作。
二.核心思路:建立“依赖联邦”
不要让你的微服务直接继承Spring Boot的官方Parent,而是继承你们项目组自定义的Parent。
这个自定义Parent负责统一管理所有依赖的版本。微服务只需要“听话”继承它,就能自动获得所有标准依赖版本,无需自己维护。
三.实战演示
1.创建一个专门的“版本管理工程”(BOM项目)
首先,创建一个空的Maven项目,不包含任何Java代码,只包含一个pom.xml。我们称之为myproject-bom。
它的作用是:定义所有微服务需要用到的第三方库版本(如MySQL, Redis, Hutool等)以及Spring Cloud组件的版本。
①定义BOM项目自身的坐标
<!-- 1.定义BOM项目自身的坐标--> <groupId>com.casually</groupId> <artifactId>myproject_bom</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging><!--pom就表明该项目只是依赖版本清单,即BOM项目,本身不会打jar、war包-->②添加说明信息(这个不写也没事,但是为了提高可读性)
<!-- 2.添加说明信息(这个不写也没事,但是为了提高可读性)--> <name>myproject_bom</name> <description>这是微服务项目的BOM项目(即:依赖清单,统一管理所有依赖的版本)</description>③依赖版本号变量定义(更加灵活,拒绝硬编码)
注意:像spring-boot.version这种变量名,是自定义的变量名,你自己知道是啥意思就行,写成123.version也行
<!-- 3.依赖版本号变量定义(拒绝硬编码)--> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <java.version>17</java.version> <!-- Spring Boot Version --> <spring-boot.version>3.3.3</spring-boot.version> <!-- Third Party Dependencies Version --> <mysql.version>8.0.28</mysql.version> <mybatis-spring-boot.version>3.0.3</mybatis-spring-boot.version> <pagehelper.version>1.4.6</pagehelper.version> <aliyun-oss.version>3.17.4</aliyun-oss.version> <jaxb.version>2.3.3</jaxb.version> <spring-security.version>3.1.5</spring-security.version> <spring-mail.version>3.1.5</spring-mail.version> <spring-data-redis.version>3.2.7</spring-data-redis.version> <jjwt.version>0.9.1</jjwt.version> <pdfbox.version>2.0.24</pdfbox.version> <langchain4j.version>1.0.1-beta6</langchain4j.version> <fastjson.version>2.0.38</fastjson.version> <nacos.version>2023.0.1.0</nacos.version> <!-- Plugin Version --> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> </properties>④核心配置:<dependencyManagement>
这是 BOM 的核心。
注意:这里只声明依赖,不引入依赖。
说白了就是:我(父工程)手里有所有的版本号(定标准),但我手里没有 jar 包(不占内存)。你们(子模块)谁想要,就自己伸手拿,拿了就是标准版,不用问版本是多少。
相当于一本书的书单,子项目要是需要某个依赖,直接引入就行,也不用写版本号,因为这里就给规定好了,哪个依赖是哪个版本。
<!-- 4.核心配置:<dependencyManagement>--> <dependencyManagement> <dependencies> <!-- Spring Boot BOM (作为基础,这个在单体项目中不存在,属于是BOM项目的特色了) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version><!--这就用到了上一步定义的依赖变量--> <type>pom</type> <scope>import</scope> </dependency> <!-- 然后把单体项目的依赖,一个一个地弄进来就行了--> <!-- Web开发--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <!-- 请使用最新稳定版 --> <scope>compile</scope> <!-- 或 optional --> </dependency> <!-- 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring-boot.version}</version> <scope>test</scope> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis-spring-boot.version}</version> </dependency> <!-- 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${pagehelper.version}</version> </dependency> <!-- 阿里云 OSS --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>${aliyun-oss.version}</version> </dependency> <!-- JAXB (Java 17 需要) --> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>${jaxb.version}</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>${jaxb-runtime}</version> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>${spring-security.version}</version> </dependency> <!-- Spring Mail --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> <version>${spring-mail.version}</version> </dependency> <!-- Spring Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>${spring-data-redis.version}</version> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>${jjwt.version}</version> </dependency> <!-- PDFBox --> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>${pdfbox.version}</version> </dependency> <!-- LangChain4j (AI) --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- Fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!-- Nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>${nacos.version}</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>${nacos.version}</version> </dependency> </dependencies> </dependencyManagement>⑤统一编译工具和打包工具的版本及配置标准,一劳永逸
<!-- 5.统一编译工具和打包工具的版本及配置标准,一劳永逸--> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${maven-compiler-plugin.version}</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> </plugin> </plugins> </pluginManagement> </build>2.将bom项目install(安装)到本地
3.在微服务项目中使用(以菜单服务menu-service为例)
注意:前提是两个项目必须使用同一个maven仓库,不然这不是纯扯淡吗,根本不可能找到。
<!-- 1.导入BOM项目(不添加依赖,而是导入版本信息)--> <dependencyManagement> <dependencies> <dependency> <groupId>com.casually</groupId> <artifactId>myproject_bom</artifactId> <version>1.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- ----------------------------------------------- --> <!-- 2.导入其他所需的依赖(但是不用声明版本了,因为1中已经规定好了)--> <dependencies> <!-- Web依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>好处:设置了BOM项目以后,微服务项目引入依赖时再也不用写版本号了,因为BOM已经事先规定好了
注意:不要把具体的依赖,放进<dependencyManagement>标签中,不然依赖只被声明,但并不会导入
四.这样做的好处
- 开发效率极高
创建新微服务时,你只需要复制一份标准的pom.xml模板,修改artifactId即可。所有依赖版本自动对齐,不需要去查单体项目用了什么版本。
- 版本一致性(防冲突)
所有微服务(用户服务、订单服务、菜单服务)都强制使用同一套依赖版本。彻底杜绝了“服务A用Jackson 2.11,服务B用Jackson 2.13”导致的序列化报错。
- 一键升级
如果Spring Boot出了安全漏洞需要升级,你只需要修改myproject-bom里的 $ {spring-boot.version}变量,然后重新发布BOM。所有微服务只需要更新父版本号,即可全部完成升级。
针对你当前情况的建议
既然你已经有了单体项目,想拆分微服务,建议按以下路径操作:
提取
打开你单体项目的pom.xml,把里面用到的所有<version>提取出来,放到一个新的myproject-bom工程的<properties>中。发布
执行mvn install,把这个myproject-bom安装到你本地的Maven仓库(或者公司的Nexus/Artifactory私服)。新建
创建menu-service,在pom.xml中<parent>指向myproject-bom(或者继承它的父工程)。清洗
在menu-service的pom.xml中,大胆删除所有依赖的<version>标签。这样,你就从“手工管理依赖”进化到了“工程化管理依赖”。
