一、背景
最近和小伙伴们合伙做一个自己的项目,处于功能模块迭代中;
后端代码基于Ruoyi进行的魔改,代码框架基本已经稳定了,而且测试环境运行OK;
spring boot版本: 2.7.15
springfox-boot-starter: 3.0.0 (包含swagger2、swagger-ui)
mapstruct: 1.5.3.Final
二、遇到问题
周六上午,有个小伙伴说他的本地项目启动时候报错了(必现问题),报错堆栈信息如下:
java.lang.NoClassDefFoundError: org/mapstruct/factory/Mappers
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestParameterMapperImpl' defined in URL
[jar:file:/D:/repo/io/springfox/springfox-swagger2/3.0.0/springfox-swagger2-3.0.0.jar!/springfox/documentation/swagger2/mappers/RequestParameterMapperImpl.class]:
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [springfox.documentation.swagger2.mappers.RequestParameterMapperImpl]:
Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/mapstruct/factory/Mappers
....省略......
Caused by: java.lang.NoClassDefFoundError: org/mapstruct/factory/Mappers
at springfox.documentation.swagger2.mappers.RequestParameterMapper.<init>(RequestParameterMapper.java:45)
at springfox.documentation.swagger2.mappers.RequestParameterMapperImpl.<init>(RequestParameterMapperImpl.java:13)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211)
... 24 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.mapstruct.factory.Mappers
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
三、问题定位
跟他确认过,本地没做其他配置修改,只是拉下来正常开发功能;
怀疑过旧编译class,进行过mavn clean,报错依旧;
确认了JDK版本,maven仓库中springfox-swagger2-3.0.0.jar大小,也一致;
理论上,不太应该呀;完全一样的代码,我本地没问题,他那边必现;
去度娘、google搜了一下,在github上有个类似issue:https://github.com/springfox/springfox/issues/3635
issue中遇到相同问题的人不少,并未找到有用的解决方案, 就CLOSE; 也看了的MapStruct的FAQ;
重新梳理了一下项目的pom文件,发现之前将mapstruct的scope为provided;
<!-- mapstruct -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<scope>provided</scope>
</dependency>
MapStruct有多种component model;
默认:需要在runtime时,引用mapstruct;即scope应该为compile;
其它(spring\jsr330等):scope可以为provided;
注意:使用spring boot maven plugin时,即使mapstruct的scope为provided,最终应用jar中也会包含mapstruct;
那么,发现一个可能,mapstruct就算配置provided,package时spring boot maven plugin也会把包打进来;但是在idea运行时,机制是不一样, idea版本不同,可能不一定会把包打进来;
我本地idea console如下:
查看本地idea运行临时jar,在MANIFEST.MF的Class-Path中,我这边可以搜索到mapstruct;另外一个小伙伴,则搜不到;
比对了idea版本,IntelliJ IDEA 2022.3 是OK的,IntelliJ IDEA 2021.3 有问题;
实锤了,不同idea版本针对mapstruct的处理机制不同;
小伙伴把他本地idea版本更新为2023.3最新版本,运行OK;
四、解决方案
从项目代码上来说,我们使用mapstruct的Default Component Model,而springfox-swagger2则使用了Default、Spring两种Model,需要在运行时依赖mapstruct;
将mapstruct的scope调整为compile(默认scope),成功解决。