首页 > 其他分享 >记一次解决SpringBoot项目由于依赖加载顺序问题导致启动报NoSuchMethodError的问题

记一次解决SpringBoot项目由于依赖加载顺序问题导致启动报NoSuchMethodError的问题

时间:2024-07-30 18:11:02浏览次数:16  
标签:WEB SpringBoot webapps jar NoSuchMethodError dassets 91 apache 加载

只发博客园,盗版必究

先说背景

平时我们的Spring Boot项目都是打成Executable Jar启动应用,最近接了个技术需求,需要打成War包,将多个项目放在同一个Tomcat中运行。

原本Jar包启动一切正常,但是打成WAR放Tomcat启动后报错了,异常栈如下:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initDdrwServiceImpl': Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.apache.commons.lang3.StringUtils.containsAny(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Z
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:138)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:422)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1698)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)

问题分析

分析一波,应该是某个依赖包里面集成了commons-lang3StringUtils工具类,但是方法不全,导致类加载器加载到该类后又找不到方法。
为了验证猜想,在报错的类构造方法中打印了下StringUtils类是从哪里加载的,代码如下:

System.out.println("StringUtils: " + StringUtils.class.getResource("").getPath());

// 输出:file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hive-exec-3.1.2.jar!/org/apache/commons/lang3/

果然,没有正确从commons-lang3加载依赖包中的工具类,而是从hive-exec依赖包中加载的。

知道了是依赖包加载的问题,我们通过打包以后的MANIFEST.MF配置文件看下,当前的Main-Classorg.springframework.boot.loader.WarLauncher,具体看了下源码(涉及到SpringBoot的启动原理,大家可以自行搜索相关文章)ExecutableArchiveLauncher中初始化了依赖资源的搜索,核心代码如下:

@Override
protected List<Archive> getClassPathArchives() throws Exception {
	List<Archive> archives = new ArrayList<>(this.archive.getNestedArchives(this::isNestedArchive));
	postProcessClassPathArchives(archives);
	return archives;
}

回到SpringBoot的Application类的main方法中,打印下URLClassLoader的资源路径,代码如下:

URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
List<URL> classLoaderUrls = Arrays.asList(classLoader.getURLs());
for(URL url : classLoader.getURLs()) {
System.out.println(url);
}

得到的结果如下:

file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/classes/
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/dropwizard-metrics-hadoop-metrics2-reporter-0.1.2.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/jaxb-impl-2.2.3-1.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hadoop-hdfs-3.3.4.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/avatica-core-1.17.0.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/ribbon-core-2.2.5.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/lucene-queries-8.5.1.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/dialect-8.6.0.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/ridl-4.1.2.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hbase-replication-2.0.0-alpha4.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/javax.el-3.0.0.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hbase-procedure-2.0.0-alpha4.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/simpleclient_tracer_otel_agent-0.12.0.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/db2jcc4-10.1.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/expiringmap-0.5.8.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hadoop-yarn-server-resourcemanager-3.3.4.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hive-llap-server-3.1.2.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/tinypinyin-2.0.3.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/transmittable-thread-local-2.12.0.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hadoop-yarn-server-common-3.3.4.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/hbase-client-2.0.2.jar
file:/Users/mac/Downloads/apache-tomcat-9.0.91/webapps/api-dassets/WEB-INF/lib/jackson-dataformat-cbor-2.9.5.jar
...以下省略...

得出两个结论:
1、资源加载是有顺序的,其中classes优先级最高,这个很重要为后面解决问题提供了思路
2、hive-exec包在commons-lang3包之前

解决思路

这里提供3种解决方案

方案一、将依赖包中的class文件在打包时放进target/classes目录下

这样就可以首先优先到正确的class文件了,maven配置如下:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>unpack-some-artifact</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>unpack</goal>
            </goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>org.apache.commons</groupId>
                        <artifactId>commons-lang3</artifactId>
                        <type>jar</type>
                        <overWrite>true</overWrite>
                        <outputDirectory>${project.build.directory}/classes</outputDirectory>
                        <includes>**/*.class</includes>
                    </artifactItem>
                </artifactItems>
            </configuration>
        </execution>
    </executions>
</plugin>

方案二、自定义Launcher

SpringBoot项目打包后Executable Jar是通过JarLauncher启动,WAR包是通过WarLauncher启动,大家可以查阅下spring-boot-loader工程看下源码。
大家可以参考JarLauncher或者WarLauncher实现自己的Launcher,Spring预留了postProcessClassPathArchives方法大家可以自由发挥。
在Maven配置文件中指定<layout>ZIP</layout>打包,Main-Class会变为org.springframework.boot.loader.PropertiesLauncher,指定loader.main参数为自己实现的ClassLoader类。

{@code loader.main}: the main method to delegate execution to once the class loader

方案三、在Application类入口处对ClassLoader中的URL重新排序

// 获取当前的ClassLoader
URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
// 获取资源列表
URL[] urls = classLoader.getURLs();

// todo urls排序

// 重置ClassLoader
URLClassLoader orderedClassLoader = new URLClassLoader(urls, classLoader.getParent());
Thread.currentThread().setContextClassLoader(orderedClassLoader);

为了保持SpringBoot工程的完整性,这里笔者选择了方法一并且验证通过,遇到同样问题的同学可以根据自己的实际情况选择解决方案

标签:WEB,SpringBoot,webapps,jar,NoSuchMethodError,dassets,91,apache,加载
From: https://www.cnblogs.com/changxy-codest/p/18332875

相关文章

  • 图片预加载和懒加载
    ......
  • springboot自学(4)自动配置原理
    自动配置原理1、收集spring开发者的编程习惯,整理开发过程使用的常用技术列表——》技术集A2、收集常用技术(技术集A)的使用参数,整理开发过程中每个技术的常用设置列表——》设置集B3、初始化springboot基础环境,加载用户自定义的bean和导入的其他坐标,形成初始化环境4、将技术集A......
  • winform界面加载慢、卡顿
    C#Winform开发窗体程序时,当控件很多时,会出现加载缓慢、闪烁的问题,或者窗体放大缩小的时候,由于控件计算比例等,也会出现显示缓慢、闪烁的问题。解决方法:///<summary>///双缓冲,解决界面加载、放大、缩小的卡顿问题///</summary>protectedo......
  • EasyExcel数据导出实现、动态表头生成、SpringBoot3框架
    1、引入EasyExcel依赖<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version></dependency>2、定义ExcelModel表单模型publicclassExcelMod......
  • 关于VMware workstation添加本地物理磁盘时候提示“未能为设备XX加载分区,权限不足,无法
    前言:玩虚拟机这么久了,原先一直是直接初始化虚拟机的时候按照流程新建虚拟机硬盘,但是正常情况下虚拟机硬盘与物理机硬盘是隔离开的,无法直接相互访问正常情况下是安装vmtools来进行相互间文件传输,或者将物理磁盘上的文件拷贝到U盘,当插上U盘的时候选择连接到对应的虚拟机即可实现......
  • 如何在 Django 中加载特定应用程序的模板?
    所以我正在高级学习Django,我已经知道如何包含manage.py所在的BASE_DIR中的模板。但是我想知道如何在Django中的特定应用程序中查找模板。例如,我有一个名为“mysite”的项目和一个名为polls的应用程序。现在,我在settings.pyDIRS=中添加了模板[os.path.join(BA......
  • Ollama+GGUF离线加载本地模型
    一般在使用Ollama下载模型时,都是从Ollama官方仓库下载(使用ollamarun命令),但一些环境下,受限于网速等原因使用这种方式可能会非常慢甚至无法下载,所以我们可以选择使用Huggingface上的GGUF文件,在Ollama仓库里的模型都可以在Huggingface上找到,因此我们可以使用Ollama+GGUF文件离线......
  • springboot校园失物招领系统-计算机毕业设计源码17082
    目 录摘要1绪论1.1研究背景1.2 研究意义1.3论文结构与章节安排2 相关技术介绍2.1B/S结构2.2SpringBoot框架2.3MySQL数据库3系统分析3.1可行性分析3.2系统流程分析3.2.1数据新增流程3.2.2 数据删除流程3.3 系统功能分析3.3.1......
  • SpringBoot2.7 霸王硬上弓 Logback1.3 → 不甜但解渴
    开心一刻一大早,她就发消息质问我她:你给我老实交代,昨晚去哪鬼混了?我:没有,就哥几个喝了点酒她:那我给你打了那么多视频,为什么不接?我:不太方便呀她:我不信,和你哥们儿喝酒有啥不方便接视频的?她:你肯定有别的女人了!我:你老公就坐在我旁边,我敢接?前情回顾SpringBoot2.7还是任性的,就......
  • 基于SpringBoot+Vue前后端分离的高校实验室预约管理系统的设计与实现
    基于SpringBoot+Vue前后端分离的高校实验室预约管理系统的设计与实现DesignandImplementationofCollegeLaboratoryReservationManagementSystembasedonSpringBootandVueforFront-End/Back-EndSeparation完整下载链接:基于SpringBoot+Vue前后端分离的高校......