记一次java.lang.ClassNotFoundException问题排查过程

news2024/9/29 3:25:28

记一次java.lang.ClassNotFoundException问题排查过程

同事提供一个or-simulation-engine.jar包(非maven项目,内部依赖很多其他jar,这个包是手动打出来的)给我,我集成到我的springboot项目中,在本地IDEA启动Springboot后,相关功能都是正常的;但是将Springboot项目打成app.jar后,使用java -jar app.jar方式启动后,运行时爆出java.lang.ClassNotFoundException: com.anylogic.libraries.modules.markup_descriptors.DescriptorFactory

为什么IDEA可以执行,打成jar包使用java -jar就执行不了呢?
以下内容都是使用java -jar app.jar测试的结果。

一、代码定位

jar包依赖关系:我的app.jar依赖第二方or-simulation-engine.jar,而or-simulation-engine.jar依赖第三方com.anylogic.engine.jar

通过分析代码得知,or-simulation-engine.jar包在运行时,调用了如下代码:

String name = "com.anylogic.libraries.modules.markup_descriptors.DescriptorFactory";
ClassLoader systemCL = ClassLoader.getSystemClassLoader();
Class clazz = systemCL.loadClass(name);

这个代码是anylogic的jar包:com.anylogic.engine.jar中的内容。

具体异常信息:

java.lang.ClassNotFoundException: com.anylogic.libraries.modules.markup_descriptors.DescriptorFactory
        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)
        at com.yonghui.or.simulation.controller.CoreController.test(CoreController.java:99)
        at com.yonghui.or.simulation.controller.CoreController$$FastClassBySpringCGLIB$$6a496143.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)

通过异常信息可以看出:ClassLoader.getSystemClassLoader()获得的是AppClassLoader,但是AppClassLoader并没有在对应的路径下加载到该类。但是该类确实是存在的,而且通过new或者Class.forname()都是可以找到该类的。

可以看到,or-simulation-engine.jar手动打完包后,内部依赖的jar都被放到了一起,不是以jar包的方式存在的。

image-20230227184433662

这可能是非maven项目or-simulation-engine手动打包有问题,导致集成到springboot项目打成app.jar后找不到该类了。

二、确定使用的ClassLoader

那么异常中的这个类com.anylogic.libraries.modules.markup_descriptors.DescriptorFactory应该使用哪个classloader加载呢?

可以使用jvm调优工具arthas,找到app.jar进程后,输入 sc -d com.anylogic.libraries.modules.markup_descriptors.DescriptorFactory命令,查看该类使用的类加载的情况:

image-20230227175936112

可以看到这里使用的是LaunchedURLClassLoader。而该加载器的上级才是AppClassLoader。

最终运行的java -jar app.jar的jar包是通过spring-boot-maven-plugin这个插件生成的, JAR中依赖的各个jar文件其实并不在运行时应用的classpath下(实际在app.jar/BOOT-INF/lib下存放所有依赖的jar包),也就是根据类加载的双亲委派机制,这些依赖没办法被默认的任何一个classloader加载,Springboot为了解决这个问题,自定义了类加载机制,LaunchedURLClassLoader就是Springboot自定义的类加载器。关于Springboot的类加载器可以查看:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
            <scope>Provided</scope>
        </dependency>

查看app.jar/META-INFO/MANIFEST.MF的内容,可以看到使用了spring-boot-loader包中的org.springframework.boot.loader.JarLauncher。这个类最后创建的就是LaunchedURLClassLoader。

image-20230227190532563

三、问题确认

通过上面的分析,可以确认问题的原因: app.jar依赖第二方or-simulation-engine.jar,而or-simulation-engine.jar依赖第三方com.anylogic.engine.jar。第三方的jar包为了代码安全,给代码做了相关的混淆等操作后,在代码运行时,使用动态ClassLoader.getSystemClassLoader()类加载器(AppClassLoader)动态加载自己的一个类;当所有的代码集成到springboot项目并用springboot-maven插件打包后,ClassLoader.getSystemClassLoader()就找不到对应的类了。
IDEA中启动不会有问题,是因为IDEA默认使用的是ApplicationClassLoader进行类加载的,而且classpath对应了多个jar包,包括jre/lib 、jre/lib/ext、本地mvn仓库、和app.jar项目路径。而java -jar方式的classpath只有app.jar。

四、解决方法

找到问题后,就可以针对性解决问题了。有两种方式:

1.改变打包方式,让打包后的代码内被ClassLoader.getSystemClassLoader()这个AppClassLoader找到;

2.修改第三方com.anylogic.engine.jar,将类加载器改成LaunchedURLClassLoader。

这两种方式的目的都是class文件放到所使用的类加载器对应的路径下。

方式一

将打包方式改成:

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>2.6</version>
				<configuration>
					<archive>
						<manifest>
							<addClasspath>true</addClasspath>
							<classpathPrefix>lib/</classpathPrefix>
							<mainClass>com.example.helloloader.HelloLoaderApplication</mainClass>
						</manifest>
					</archive>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<version>2.10</version>
				<executions>
					<execution>
						<id>copy</id>
						<phase>package</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
						<configuration>
							<outputDirectory>
								${project.build.directory}/lib
							</outputDirectory>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

方式二

修改第三方com.anylogic.engine.jar,将类加载器改成LaunchedURLClassLoader。其实就是修改一行代码:

将:

ClassLoader systemCL = ClassLoader.getSystemClassLoader();

修改成:

 ClassLoader systemCL = Thread.currentThread().getContextClassLoader();

这样app.jar在运行时就可以获得LaunchedURLClassLoader。

这种方式需要反编译,如果将第三方com.anylogic.engine.jar整体反编译,部分class会编译失败,修改代码后也很难再编译成功。

这里有个简单的方式:新建一个空maven项目,在pom中引入本地的com.anylogic.engine.jar。然后按照要修改的class文件在第三方jar包内的包名,在该空项目中建相同的包和类名(com.anylogic.engine.markup.descriptors.IDescriptorFactory),并将反编译后的内容放入这个类中,再修改掉对应的一行代码。通过mvn clean packge重新打包,在target目录下找到这个IDescriptorFactory.class文件。

image-20230227193716288

然后用这个IDescriptorFactory.class替换掉第三方com.anylogic.engine.jar所对应的IDescriptorFactory.class.

如何替换?

首先解压:

 jar -xvf com.anylogic.engine.jar

然后找到并替换掉IDescriptorFactory.class

最后成jar包

 jar cvfM  com.anylogic.engine.jar ./

将新的jar包集成到项目中后就可以启动了。

参考:

https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html#appendix.executable-jar.restrictions

https://blog.csdn.net/wuxiaolongah/article/details/129245218

https://blog.csdn.net/kingwinstar/article/details/125482503

https://www.jianshu.com/p/1ec1189f2397

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375491.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网上招聘系统

技术&#xff1a;Java、JSP等摘要&#xff1a;当今&#xff0c;人类社会已经进入信息全球化和全球信息化、网络化的高速发展阶段。丰富的网络信息已经成为人们工作、生活、学习中不可缺少的一部分。人们正在逐步适应和习惯于网上贸易、网上购物、网上支付、网上服务和网上娱乐等…

C#多窗口切换

多窗口切换【功能目标】1、实现多窗口切换&#xff08;Panel&#xff09;2、动态生成窗口内文本框以及标签&#xff08;重点&#xff09;3、改变文本框内容【效果图】【代码详解】1、多窗口切换如要实现多窗口切换&#xff0c;需要用到Panel&#xff0c;对于这个控件不熟悉的可…

正演(1): 二维声波正演模拟程序(中心差分)Python实现

目录 1、原理&#xff1a; 1&#xff09;二维声波波动方程: ​编辑 2&#xff09;收敛条件&#xff08;不是很明白&#xff09; 3&#xff09;雷克子波 4&#xff09;二维空间衰减函数 5&#xff09;边界吸收条件 (不是很明白。。) 2、编程实现 1&#xff09;参数设置&…

RNN相关知识总结

目录RNN结构与原理1.模型总览2.反向传播LSTM结构与原理1.模型总览2.如何解决RNN梯度消失/爆炸问题&#xff1f;GRU结构及原理1.模型总览LSTM与GRU的区别RNN结构与原理 1.模型总览 上图是RNN的展开结构图&#xff0c;由输入层、隐藏层和输出层组成。当前时间步t 的隐藏状态hth_…

Spark 分析计算连续三周登录的用户数

前言&#xff1a;本文用到了窗口函数 range between&#xff0c;可以参考这篇博客进行了解——窗口函数rows between 、range between的使用 创建数据环境 在 MySQL 中创建数据测试表 log_data&#xff1a; create table if not exists log_data( log_id varchar(200) comm…

能在软路由docker给部署搭建teamsperk服务器么?并且设置好ddns

参考链接(4条消息) 【个人学习总结】使用docker搭建Teamspeak服务器_blcurtain的博客-CSDN博客_teamspeak3 docker(⊙﹏⊙)哎呀&#xff0c;崩溃啦&#xff01; (tdeh.top)TeamSpeak服务器搭建与使用 - 缘梦の镇 (cmsboy.cn)Openwrt X86 docker运行甜糖-软路由,x86系统,openwrt…

(四)K8S 安装 Nginx Ingress Controller

ingress-nginx 是 Kubernetes 的入口控制器&#xff0c;使用NGINX作为反向代理和负载均衡器 版本介绍 版本1&#xff1a;Ingress NGINX Controller(k8s社区的ingres-nginx) 以 NGINX 开源技术为基础&#xff08;kubernetes.io&#xff09;&#xff0c;可在GitHub的 kubernet…

如何创建并管理一个刷题小组?

“如何收回用户对题库的使用权”&#xff0c;这是一个大多数题库创建人都会碰到的管理问题&#xff0c;也是日常咨询频繁的问题。土著刷题在v1.10版本已经上线了小组模块功能&#xff0c;小组拥有丰富的用户管理功能&#xff0c;可以管理组员对于题库的使用权进行有效的管理。咱…

高压放大器在应力波法套筒灌浆密实度检测研究中的应用

实验名称&#xff1a;高压放大器在应力波法套筒灌浆密实度检测研究中的应用研究方向&#xff1a;无损检测测试目的&#xff1a;钢筋套筒灌浆连接技术被广泛应用于装配式建筑节点连接中&#xff0c;但灌浆不密实将导致节点失效的风险。因此&#xff0c;施工中对套筒灌浆的密实度…

使用xca工具生成自签证书

本文使用 xca 生成自签证书。 概述 之前使用 openssl 生成证书&#xff0c;在 golang 中测试&#xff0c;发现客户端连接失败&#xff0c;经查发现是Subject Alternative Name不支持导致的。因虚拟机 openssl 版本较低&#xff0c;有个功能无法实现&#xff0c;且升级麻烦&…

SAP SD模块学习总结2 2023.2.27

https://www.cnblogs.com/jiangzhengjun/p/7264657.html#_Toc410466840 首先是表&#xff1a; VBAK: 销售订单抬头 VBAP: 销售订单项目 VBUK: 抬头状态 VBUP: 行项目状态 VBKD:销售凭证&#xff1a; 业务数据 VBPA: 销售凭证: 合作伙伴 VBEP&#xff1a;销售凭证&#xff1a;…

【论文速递】COLING 2022 - 带有事件论元相关性的事件因果关系抽取

【论文速递】COLING 2022 - 带有事件论元相关性的事件因果关系抽取 【论文原文】&#xff1a;Event Causality Extraction with Event Argument Correlations 【作者信息】&#xff1a;Cui, Shiyao and Sheng, Jiawei and Cong, Xin and Li, Quangang and Liu, Tingwen and S…

Android NDK动态加载SO库

背景对于一个普通的android应用来说&#xff0c;so库的占比通常都是巨高不下的&#xff0c;因为我们无可避免的在开发中遇到各种各样需要用到native的需求&#xff0c;所以so库的动态化可以减少极大的包体积&#xff0c;自从2020腾讯的bugly团队发部关于动态化so的相关文章后&a…

fuse:纠结的page下刷流程之fuse_writepage_in_flight

fuse&#xff1a;纠结的page下刷流程细节之fuse_writepage_in_flightfuse_writepage_in_flight硬爬代码自己理解消化作者本人如是说fuse_writepage_in_flight 先说下这个函数&#xff0c;位于fs/fuse/file.c&#xff0c;这里以4.19内核来分析。因为这个函数里面藏了很多小细节…

华为OD机试模拟题 用 C++ 实现 - 删除指定目录(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明删除指定目录题目输入输出示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为…

Macbook M1 安装PDI(Kettle) 9.3

Macbook M1 安装PDI(Kettle) 9.3 当前 PDI&#xff08;Kettle&#xff09;最新版为9.3&#xff0c;依赖Java JDK 11。因为没有专门用于 M1的程序&#xff0c;需要下载并安装x86_64架构的JDK及依赖软件&#xff0c;并 “强制在Intel模式下运行shell” 的方式来实现 Kettle 的正…

骨传导蓝牙耳机排行,盘点几款性能不错的骨传导耳机

随着蓝牙耳机的普及&#xff0c;骨传导耳机也越来越受到欢迎&#xff0c;很多人也都开始在了解并尝试骨传导耳机。相比于其他类型耳机&#xff0c;在舒适度、安全方面有一定优势。尤其是在户外运动时&#xff0c;或者长时间佩戴运动时&#xff0c;使用骨传导耳机可以避免耳朵因…

从“入门”到“专家”,一份3000字完整的性能测试体系的知识分享

随着科技的飞速发展&#xff0c;软件产品广泛应用于各个行业领域&#xff0c;人们对计算机和网络的依赖性越来越大&#xff0c;对新奇事物也越来越感兴趣&#xff0c;成千上万的用户活跃在庞大的网络系统中&#xff0c;这给提供服务的系统带来严重的负荷&#xff0c;"高并…

QT之图形视图框架概述——Graphics View Framework

QT之图形视图框架概述——Graphics View Framework1. 概述2. 核心类3. 事件传递4. Graphics View 坐标系统5. 参考1. 概述 Graphics View Framework是子Qt 4.2引入的&#xff0c;用来取代之前版本中的QCanvas。Graphics View Framework提拱了用于大量2D图形项的管理和交互的能…

Spring Boot 统一功能处理(用户登录权限效验-拦截器、异常处理、数据格式返回)

文章目录1. 统一用户登录权限效验1.1 最初用户登录权限效验1.2 Spring AOP 统一用户登录验证1.3 Spring 拦截器1.4 练习&#xff1a;登录拦截器1.5 拦截器实现原理1.6 统一访问前缀添加2. 统一异常处理3. 统一数据格式返回3.1 统一数据格式返回的实现3.2 ControllerAdvice 源码…