SpringBoot版本升级引起的FileNotFoundException——WebMvcConfigurerAdapter.class

那年烟雨落申城
• 阅读 143

缘起

最近公司项目要求JDK从8升到17,SpringBoot版本从2.x升级到3.x,期间遇到了一个诡异的FileNotFoundException异常,日志如下(敏感信息使用xxx脱敏)

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.xxx.xxx.OperationAnalysisApplication]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:179)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:397)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:283)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
    at com.xxx.xxx.OperationAnalysisApplication.main(OperationAnalysisApplication.java:34)
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.class] cannot be opened because it does not exist
    at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:211)
    at org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:54)
    at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:48)
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103)
    at org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory.createMetadataReader(ConcurrentReferenceCachingMetadataReaderFactory.java:86)
    at org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory.getMetadataReader(ConcurrentReferenceCachingMetadataReaderFactory.java:73)
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81)
    at org.springframework.context.annotation.ConfigurationClassParser.asSourceClass(ConfigurationClassParser.java:611)
    at org.springframework.context.annotation.ConfigurationClassParser$SourceClass.getSuperClass(ConfigurationClassParser.java:924)
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:335)
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:244)
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:189)
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:298)
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:244)
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:197)
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:165)
    ... 13 common frames omitted

SpringBoot3.x已经删除了类WebMvcConfigurerAdapter,建议实现接口WebMvcConfigurer

过程

1. 查找当前项目中使用WebMvcConfigurerAdapter的地方

这个报错日志明显没有提示出哪个类有问题,先排除当前项目自己写的代码有没有这个类,于是在IDEA中使用全局查找来查找使用WebMvcConfigurerAdapter的地方,发现都被WebMvcConfigurer接口替代了,看来并不是自己写的代码里面的,二是某个Jar中的,这大概就是没有显示因为哪个类继承了WebMvcConfigurerAdapter导致异常的原因吧

2. 查找哪个Jar中引用了这个类

下个断点看看

这TM和大海捞针没区别啊,这项目光依赖自己的包就几十个,大部分都是公司内部包,我都没有源码,这可怎么找啊,只能去报异常的地方碰碰运气了。我找到了报异常的地方: 日志显示在这

at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:211)

源码如下:

public InputStream getInputStream() throws IOException {
   InputStream is;
    if (this.clazz != null) {
        is = this.clazz.getResourceAsStream(this.path);
    } else if (this.classLoader != null) {
        is = this.classLoader.getResourceAsStream(this.absolutePath);
    } else {
        is = ClassLoader.getSystemResourceAsStream(this.absolutePath);
    }

    if (is == null) {
        throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
    } else {
        return is;
    }
}

报异常的地方很明显了,就是这

 if (is == null) {
     throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
 }

在这里下断点,启动项目,发现这个类里this.clazz是null,this.classLoader不是null,感觉这个classloader应该就是加载某个类的时候发现它的父类是WebMvcConfigurerAdapter,去加载WebMvcConfigurerAdapter找不到才报错的。就去这个classloader里面翻找了一下,感觉最有价值的就是里面的classes属性了,如下: SpringBoot版本升级引起的FileNotFoundException——WebMvcConfigurerAdapter.class

当前实例中的classes中找找

里面居然已经加载了5285个类,一个一个的看显然不行,于是在classes上右键,然后选择评估表达式,想着这个classes是个ArrayList,使用classes.stream().filter()筛选一下加载的类里面是否有父类是WebMvcConfigurerAdapter的,找到了不就知道是哪个有问题了么。写好筛选条件后执行,报错如下: SpringBoot版本升级引起的FileNotFoundException——WebMvcConfigurerAdapter.class 这.....大概是JDK里面很多类还没加载导致的吧,看来这条行不通了。于是考虑这个classesArrayList类型,有序列表啊,其中最后一个元素有没有可能是当前继承了WebMvcConfigurerAdapter的类么?于是使用评估表达式获取了最后一个元素,很遗憾,是个idea的类: SpringBoot版本升级引起的FileNotFoundException——WebMvcConfigurerAdapter.class 梦想再一次破灭了。

是不是当前类加载器能提供点什么信息

想着是不是当前类加载器能看到当前在加载哪个类或者哪个类的加载引起了当前类的加载啊?很遗憾,classloader根本没有这信息,只有当前类加载器正在加载的这个类WebMvcConfigurerAdapter

看看谁触发了当前类加载

很遗憾,上面的方法都没找到很有用的信息,于是想着,看看谁调用了当前方法,也就是说谁触发了加载WebMvcConfigurerAdapter这个类的动作,于是顺着调用栈: SpringBoot版本升级引起的FileNotFoundException——WebMvcConfigurerAdapter.class 一直往回找,终于,在下面这个地方找到了: SpringBoot版本升级引起的FileNotFoundException——WebMvcConfigurerAdapter.class 这里点开resource字段,直接定位到类:

import com.xxx.data.analytics.interceptor.RequestDataInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    public InterceptorConfig() {
    }

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestDataInterceptor()).addPathPatterns(new String[]{"/**"});
        super.addInterceptors(registry);
    }
}

就是这了。 Jar里面没法排掉某个特定的类,只能在当前项目中创建一个和这个类一模一样的类,代码改成自己想要的。因为Java中,某个类(同一个classloader+全限定名)已经被加载了,就不会再次被加载,项目中的类加载优先于Jar包中的,又都是同一个类加载器加载,所以会优先加载到项目中类,Jar中的则不会被加载了,曲线救国 我试了下似乎不行,待考证,暂时先加删除线,后续会回来看看怎么解决这个问题。 -------------------------------------------------------->分割线20230729<------------------------------------------------------------------------------ 最后在项目里创建了一个package :org.springframework.web.servlet.config.annotation然后把WebMvcConfigurerAdapter拷贝进来,把InterceptorConfig在SpringBoot启动类上设置为不扫描,把问题解决了。不创建包org.springframework.web.servlet.config.annotation并拷贝WebMvcConfigurerAdapter直接在SpringBoot启动类上设置为不扫描是不行的,因为你设置InterceptorConfig不扫描时JVM会首先加载这个类,这个类有父类就先尝试加载父类WebMvcConfigurerAdapter,父类不存在,直接报错。

点赞
收藏
评论区
推荐文章
Stella981 Stella981
2年前
C# Aspose.Cells导出xlsx格式Excel,打开文件报“Excel 已完成文件级验证和修复。此工作簿的某些部分可能已被修复或丢弃”
报错信息:最近打开下载的Excel,会报如下错误。(xls格式不受影响)!(https://oscimg.oschina.net/oscnet/2b6f0c8d7f97368d095d9f0c96bcb36d410.png)!(https://oscimg.oschina.net/oscnet/fe1a8000d00cec3c
Stella981 Stella981
2年前
Linux查看GPU信息和使用情况
1、Linux查看显卡信息:lspci|grepivga2、使用nvidiaGPU可以:lspci|grepinvidia!(https://oscimg.oschina.net/oscnet/36e7c7382fa9fe49068e7e5f8825bc67a17.png)前边的序号"00:0f.0"是显卡的代
Wesley13 Wesley13
2年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
2年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
2年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
那年烟雨落申城
那年烟雨落申城
Lv1
男 · 众安科技 · 高级Java开发工程师
是你吧,我能从很远很远的地方一眼认出你来
文章
25
粉丝
0
获赞
1