问题
开发平台在集成minio时,pom引入了sdk。
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.7</version>
</dependency>
在调用上传文件API时,控制台报错:
从错误来看,是okhttp的版本过低,minio要求版本4.11.0及以上.
初看这错误一脸懵,okhttp是minio内部依赖的jar包,难不成其他地方也在使用,造成了版本冲突?
排查
使用maven helper插件提供的jar包依赖分析,发现并没有出现okhttp冲突的情况,如下图:
然后整个项目工程中搜索okhttp关键字,任何模块没有显性使用过。
查看使用minio组件模块的依赖,确认当前使用的okhttp的版本是3.14.8,明显低于控制台中提示的版本4.11.0。
这就奇怪了,其他模块没有使用okhttp,为什么会出现了与minio要求不一致的版本呢?
尝试
调整使用minio组件的oss模块的pom文件,将minio的引用中排除okhttp,然后再单独添加对okhttp4.12.0版本的依赖。
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.7</version>
<exclusions>
<exclusion>
<artifactId>okhttp</artifactId>
<groupId>com.squareup.okhttp3</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
使用maven clean install,查看依赖的okhttp,仍然是旧版本,上述调整并没有生效。
注:网上不少文章都是这么做的,实际并不能解决问题。
对比
与原来做技术验证的miniodemo项目对比,查看依赖的okhttp包,其版本是正常的,如下图所示:
此对比确认minio组件自身没有问题,基本确定问题根源在于开发平台的环境上。
定位
问题排查到这一步,基本能定位根源了,就是开发平台使用了spring-boot-dependencies来管理组件内部的依赖关系。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
这么做是因为众多的组件,常常会因为版本不兼容引发冲突和异常,spring-boot-dependencies会将经过严格的兼容性测试的组件版本进行统一维护和管理。附带的另一个好处是,引用组件时只需要指定groupId和artifactId,version属性可以省略。
在spring-boot-dependencies的pom文件中搜索okhttp,果然发现里面定义的版本3.14.8,与上面的有冲突的版本一致。
解决
问题根源找到了,解决自然也容易。在项目的根pom中,引入okhttp,指定版本,这么做会覆盖spring-boot-dependencies的版本设置,但同时也意味着需要你自行确保新版本的okhttp与其他组件不存在冲突。
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!--spring boot dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
……
<!--强制指定版本,优先级高于spring-boot-dependencies-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
</dependencies>
</dependencyManagement>
反思
实际该情况并不是第一次遇到,之前lombok组件也曾出现过类似的问题,其现象是编辑器报类似如下警告: java: You aren’t using a compiler supported by lombok, so lombok will not work and has been disabled. Your processor is: com.sun.proxy.$Proxy8 Lombok supports: OpenJDK javac, ECJ。
按照上述方式设置了lombok的新版本后,问题解决。
同时,需要注意这里有个反常识的地方,即全局的依赖管理,优先级要高于具体模块的pom配置。
知识延伸
java中库特别多,版本也很多,组合使用是存在版本冲突问题。例如在使用Spring的时候,经常会使用到第三方库,一般大家都是根据经验挑选一个版本号或挑选最新的,随意性较大,其实这是有问题的,除非做过完整的测试,保证集成该版本的依赖不会出现问题,且后续集成其它第三方库的时候也不会出现问题,否则风险较大,且后续扩展会越来越困难,因为随着业务复杂度的增加,集成的第三方组件会越来会多,依赖之间的关联也会也来越复杂。
除了本文提到的用于springboot项目的spring-boot-dependencies外,还有两个常用的版本依赖管理组件。
io.spring.platform适用于spring大家族。
spring-cloud-dependencies适用于spring-cloud微服务框架。
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Cairo-SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
需要注意的是,以上两个依赖管理库,只负责自己发布的库及相关库,并不能覆盖所有库,因此,部分库还得写版本号。
同理,使用了这两个组件来管理依赖,也会发生本文类似问题。