记录自己遇见的一次ClassNotFoundException

问题来源

记录今天遇到的一个NoClassNotFoundException.

2023-11-10 11:08:52,386 ERROR [dispatcherServlet]:175 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: ma/glasnost/orika/MapperFactory] with root cause
java.lang.ClassNotFoundException: ma.glasnost.orika.MapperFactory
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[?:1.8.0_301]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[?:1.8.0_301]
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[?:1.8.0_301]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[?:1.8.0_301]
	at com.patsnap.data.common.lake.query.LakeQueryTool.post(LakeQueryTool.java:373) ~[data-common-cloudApi-Dev.0.2.5-20231110.020456-22.jar:Dev.0.2.5-SNAPSHOT]
	at com.patsnap.data.common.lake.query.LakeQueryTool.getDPPToken(LakeQueryTool.java:120) ~[data-common-cloudApi-Dev.0.2.5-20231110.020456-22.jar:Dev.0.2.5-SNAPSHOT]
	at com.patsnap.data.common.lake.query.LakeQueryTool.athenaQueryLogIdAsync(LakeQueryTool.java:179) ~[data-common-cloudApi-Dev.0.2.5-20231110.020456-22.jar:Dev.0.2.5-SNAPSHOT]
	at com.patsnap.data.common.lake.query.LakeQueryTool.athenaQuery(LakeQueryTool.java:312) ~[data-common-cloudApi-Dev.0.2.5-20231110.020456-22.jar:Dev.0.2.5-SNAPSHOT]
	at com.patsnap.data.common.lake.query.LakeQueryTool.athenaQuery(LakeQueryTool.java:298) ~[data-common-cloudApi-Dev.0.2.5-20231110.020456-22.jar:Dev.0.2.5-SNAPSHOT]
	at com.patsnap.data.exploration.utils.DppApi.athenaQuerySync(DppApi.java:171) ~[classes/:?]
	at com.patsnap.data.exploration.controller.DataExplorationController.executeAthenaSql(DataExplorationController.java:207) ~[classes/:?]

事情是酱紫的~今天在公司写了一个公共模块Common-A,然后需要把这个Common模块用于其他的业务项目中,A模块中需要使用反射工具类来进行对象字段直接的get,set赋值(没有引入spring,只是使用java本身自带的反射类)。但是A模块为业务模块,像反射这种和业务无关的功能应该写在与业务无关的Common的moudle下,因此我在Common-core模块下,引入了反射包orika-core。maven依赖如下。

<dependencies>
    <dependency>
        <groupId>ma.glasnost.orika</groupId>
        <artifactId>orika-core</artifactId>
    </dependency>
    ...
</dependencies>

Common-core又是依赖父module, Common-parent的,因此orika-core这个依赖的版本也是写在 Common-parent里面进行统一版本管理的,如下。

<properties>        
    <ma.glasnost.orika.orika-core.version>1.4.6</ma.glasnost.orika.orika-core.version>
    ...
</properties>

<dependencyManagement>
    <dependencies>
        <!--            orika-core: 反射工具-->
        <dependency>
            <groupId>ma.glasnost.orika</groupId>
            <artifactId>orika-core</artifactId>
            <version>${ma.glasnost.orika.orika-core.version}</version>
        </dependency>
        ....
    </dependencies>
</dependencyManagement>

现在大致依赖情况是这样的。

公司的业务项目 --> Common-A —> Common-core(写了个反射工具类,引入了orika-core, orika-core的版本管理在Common-parent里面(这是重点后面要考!))

按理来说maven默认的dependency,父项目中有的依赖,在子项目中也会引入,就是说依赖了Common-A的业务项目也应该会有orika-core这个jar包才对(幻想很美好,现实很骨感)。

排查过程

一开始遇到这个Exception很懵逼,主要是因为快下班了。。。。。,后来洗把脸冷静了下,慢慢开始屡屡逻辑。我现在业务代码执行的必经之路上打了断点调试,看看当前的ClassLoader中有哪些classes,虽然知道应该没有ma.glasnost.orika.MapperFactory这个类了,但还是想看看。然后

犹豫了片刻,嗯。。。。,8000多个类,这得找到猴年马月,然后就想着在业务的毕竟之路上,写一段逻辑来筛选出我想要的class类。代码如下。

ClassLoader classLoader = lakeQueryTool.getClass().getClassLoader();
Field classes = classLoader.getClass().getDeclaredField("classes");
classes.setAccessible(true);
Vector<Class> o = (Vector<Class>) classes.get(classLoader);
for (Class aClass : o) {
    if (aClass.getCanonicalName().contains("patsnap")) {
        System.out.println(aClass.getCanonicalName());
    }
}

但是好像没办法获取到classes这个字段的内容。。。。是我在异想天开

后来想到了利用ClassLoader的url来搜寻项目中jar包,然后查看jar包内class的方法,代码入下:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
URL[] urls = urlClassLoader.getURLs();
for (URL url : urls) {
    if (url.toString().contains("patsnap")) {
        JarFile jarFile = new JarFile(url.getFile());
        jarFile.stream().filter(entry -> entry.getName().endsWith(".class")).forEach(entry -> {
            String className = entry.getName().replace("/", ".").replace(".class", "");
            System.out.println(className);
        });
        System.out.println(url);
    }
}

上面的代码成功的实现了,我的想法,看到了当前项目中所依赖的和公司有关的class类,然后我也寻找了ma.glasnost.orika.MapperFactory这个类。确实是没有找到

然后想到是不是在maven打包的时候压根就没有把这个依赖打包到项目里面,开始找pom.xml的茬,终于发现了,在我业务项目所依赖的Common-core的pom.xml里面的orika-core依赖没有引入成功,因为没有版本号,版本号是在Common-parent这个父项目里面统一进行管理的,但是我maven打包install的时候我只install了Common-A和Common-core,Common-parent的pom还是之前的,之前并没有这个版本号,这个版本号时候后来加入的。。。。。。

因此破案了,就是我maven,install的时候没有install, Common-parent这个模块导致,没有导入orika-core的相关依赖。

虽然但是很激动的,主要是自己一步步排查找到了问题所在(绝对不是因为可以下班了)。

经此之后,写公共模块,如果改动好几个模块一定要仔细,install,deploy的时候一定要把改动的模块都关照到。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值