Hello,这里是爱 Coding,爱 Hiphop,爱喝点小酒的 AKA 柏炎。
对于工作1-3年的’小白‘java程序员
而言,日常工作大多接触的都是业务开发。
而在业务开发中更多都是对PM提出的需求进行CRUD的code转化,没有形成一套自有的技术体系。
对于小公司的业务开发,可能还会接触到小部分底层组件的开发。大公司的程序员更多是做好螺丝钉的角色,公司都会配备基础架构团队,业务开发只要无脑使用基础架构团队提供的脚手架与各种sdk就能轻松的完成日常的业务开发。
久而久之,你会感觉你工作3年跟工作1年的时候,除了对业务更加了解,CRUD与CV更加快了以外。技术上的成长微乎其微。
为了突破上述的困局,特开一个专栏来讲解如何从0到1搭建起一个基础架构。
其实我本来想放弃这个专栏,出一本小册的,奈何签约了,那就硬着头皮上吧。
一、混乱的版本依赖
对于中小型公司而言,为了抢占某个行业市场,经常会提倡内部做敏捷开发
。
怎么做到快?
引入一些成熟的开源组件,例如hutool、fastjson、easyexcel等。
而在spring的体系下,我们引用这些组件的首选方式就是使用Maven依赖。
这些开源组件因为使用的人多了,又是开源项目,因此频繁会爆出各种漏洞。
FASTJSON反序列化远程代码执行漏洞通告 5 分钟复现 log4J 漏洞,手把手实现
当前市面上主流的微服务体系下,为了让服务的业务边界更加清晰,往往会拆分很多服务。一旦一些工具类型的开源组件爆出漏洞,升级修改将是一个非常痛苦的过程。
我记得log4j这个bug爆出来的时候,网上真的哀嚎一片,很多程序员都需要加班去升级log4j的组件重新发布。考虑到移除工具可能会引入大量的代码修改工作,自然就选择了版本升级的解决方式。但是这种方式的麻烦点在于,需要一个个业务包修改。如果发现了其他的安全漏洞依赖,也需要对其进行修改。虽然不是什么高难度棘手的问题,但是一个个工程的修改,工作就很重复,我有这时间,多摸一会儿鱼,他不香吗?
因此,建立一个统一的版本依赖体系是尤其重要的。
二、maven依赖的优先级
在构建maven的依赖体系之前我们先来熟悉一下maven是如何处理依赖的优先级的。
2.1.最短路径优先
工程中依赖了B、C两个jar包。
在B jar包内引用了C jar包版本为1.0
在工程内直接引用的C jar包版本为2.0
Project->B->C(1.0) , Project->C(2.0)。由于C(2.0)路径最短,所以项目使用的是C(2.0)。
2.2.pom申明顺序优先
如果project->B->C(1.0) ,project->D->C(2.0) 这样的路径长度一样怎么办呢?
这样的情况下,maven会根据pom文件声明的顺序加载,如果先声明了B,后声明了D,那就最后的依赖就会是C(1.0)。
三、maven包版本控制
spring组件库多模块 (模块间独立) 的情况下,每个模块可能都需要引用fastjson这个序列化组件。为了统一各个组件引入的fastjson版本,我们该怎么做呢?
3.1.人为约定
在每个子模块的pom中添加如下依赖
代码语言:javascript复制<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.43</version>
</dependency>
这种方式更多是人为规范约束,各个模块的负责人只要有一个不守武德,那么各个模块之间的底层组件版本依赖就会不一致。
没错,麻瓜行为。
3.2.使用dependencyManagement标签
maven中的版本控制依赖dependencyManagement标签
我们在顶层的父pom中可以定义如下的三方jar的版本定义
代码语言:javascript复制<dependencyManagement>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.43</version>
</dependency>
</dependencyManagement>
这样需要引用这个jar的子模块可以忽略版本定义直接引用
代码语言:javascript复制<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
四、顶层版本管理
我们随便打开一个springboot的项目,打开到pom依赖。
pom的父级依赖都是
代码语言:javascript复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${springboot-version}</version>
</parent>
点击这个parent的依赖进去看到顶级的父级依赖为
代码语言:javascript复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot-version}</version>
</parent>
嗯哼?这个父级名字是不是很玄妙?
继续点进去
里面已经没有任何jar的实际引用了,只有各种各样的springboot或者spring生态可能会依赖到的jar包的版本定义。
到这里,everybody有什么想法或者感悟?
spring通过定义一个顶层的父级版本依赖,只要是符合springboot大版本下的spring组件内的各个jar版本都是统一的。
如果出现依赖升级的情况,不需要再去升级一个个组件的版本,直接升级父级的依赖管理pom中的版本即可。
妙啊~
五、多项目全局管理
有了spring给我们maven版本管理的思路,那我们是不是也可以定义这样一个业务的顶层maven版本管理工程
依赖管理demo : common-dependency
接着这个思想,我们也来新建一个空的工程,专门用来管理maven版本
代码语言:javascript复制├── pom.xml
└── baiyan-common-dependency.iml
具体操作
1.版本管理工程的pom的父pom依赖spring-boot
2.版本管理工程的pom内定义业务通用的一些maven依赖版本
3.推送该工程至公司中央仓库(本地测试可以直接执行maven install将pom打包到本地私仓)
4.业务应用将父pom从spring-boot切换为baiyan-common-dependency
这样类似于一些springboot的版本,fastjson的版本等等比较具有通用性质的依赖版本定义都可以放在单独的依赖管理工程中,一旦依赖出现安全问题,只要一处地方修改就可以。
这时有的同学应该会有这样的疑问,我想用springboot-web的版本是2.3.8.RELEASE,但是在依赖工程中的版本已经变成2.3.10.RELEASE。
这咋办?
那么根据最短路径依赖优先级你只需要在业务应用的父pom中dependencyManagement标签内定义springboot-web的版本为2.3.10.RELEASE。
灵活性也是港港的,非常的nice。
六、总结
本篇是从0到1搭建基础架构系列的第一篇,着重为大家介绍了如何使用maven来统一管理多模块,多服务的三方jar版本。详细介绍如何将零散的、独立的依赖版本维护到一个统一的地方,为后面搭建起一套通用的基础架构打下基础。业务模块、服务中我们需要单独引入的三方依赖也可以利用maven的版本优先级在父pom来统一管理。
最后给出我所认为的maven依赖管理的最佳实践
1.定义一个最父级的maven版本依赖管理工程,内部包含所有通用的工具类,业务组件的版本定义(例如mysql、fastjson版本)
2.业务服务中parent依赖的springboot的父级依赖替换为自定义的maven项目
3.业务服务是多模块的情况下,所有未在最父级maven版本依赖内定义的jar
或者业务模块就是需要使用独立版本的jar
统一定义在业务服务的顶级父pom中
这样即保证了依赖的版本的统一维护,也有业务使用的灵活性。
顶级模块demo参考:common-dependency 业务模块demo参考(该项目为后续基础架构的演示项目):common-frame