记一次springboot项目结合arthas排查ClassNotFoundException问题

贾珖
• 阅读 1185

前言

前阵子业务部门的项目出现了一个很奇怪的问题,有个class明明存在,本地idea运行也没问题,然后一发布线上就出现ClassNotFoundException问题,而且线上这个class确实是存在的。本文就通过一个demo示例来复现这么一个情况

demo示例

注: 本文的项目框架为springboot2。本文仅演示ClassNotFoundException相关内容,并不模拟业务流

业务服务A

package com.example.helloloader.service;

import org.springframework.stereotype.Service;

@Service
public class HelloService {

    public String hello(){
        return "hello loader";
    }
}

组件B

@Component
public class HelloServiceLoaderUtils implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public String invoke(String className){
        try {
            ClassLoader classLoader = ClassLoader.getSystemClassLoader();
            Class clz = classLoader.loadClass(className);
            Object bean = applicationContext.getBean(clz);
            Method method = clz.getMethod("hello");
            return (String) method.invoke(bean);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

服务A调用组件B

@SpringBootApplication(scanBasePackages = "com.example")
public class HelloLoaderApplication implements ApplicationRunner {

    @Autowired
    private HelloServiceLoaderUtils helloServiceLoaderUtils;

    public static void main(String[] args) {
        SpringApplication.run(HelloLoaderApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(helloServiceLoaderUtils.invoke(HelloService.class.getName()));
    }
}

异常复现

如果通过本地idea进行调用,控制台会正常打印出

hello loader

将业务服务A打包,通过

java -jar hello-loader-0.0.1-SNAPSHOT.jar

启动访问
记一次springboot项目结合arthas排查ClassNotFoundException问题
出现了ClassNotFoundException异常

异常排查

class存在,却找不到class,要么就是类加载器错了,要么是class的位置错了。因此通过arthas进行排查。对arthas不了解的朋友,可以查看如下文章
java应用线上诊断神器--Arthas

我们通过如下命令查看com.example.helloloader.service.HelloService加载器

 sc -d com.example.helloloader.service.HelloService

记一次springboot项目结合arthas排查ClassNotFoundException问题
从图片可以看出打包后的HelloService的类加载器为spring封装过的加载器,因此用appClassLoader是加载不到HelloService

解决方法

1、方法一将appClassLoader改成spring封装的加载器

做法就是将ClassLoader.getSystemClassLoader()改成
Thread.currentThread().getContextClassLoader()即可

改好重新打包。此时重新运行,观察控制台

当前类加载器:org.springframework.boot.loader.LaunchedURLClassLoader@439f5b3d
hello loader
2、方法二修改打包方式

将打包插件由

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

切换成

    <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>

切换打包方式后,重新运行

当前类加载器:sun.misc.Launcher$AppClassLoader@55f96302
hello loader

此时正常输出,且加载器为AppClassLoader。我们可以通过

sc -d com.example.helloloader.service.HelloService

观察HelloService的类加载器

记一次springboot项目结合arthas排查ClassNotFoundException问题
此时的HelloService的类加载器为AppClassLoader

总结

1、如果项目采用springboot的打包插件,他的class会放在/BOOT-INF,且该目录下的class类加载器为

org.springframework.boot.loader.LaunchedURLClassLoader

2、arthas是个好东西,谁用谁知道

3、当时业务排查的时候,过程是比我文章示例还要复杂一点。因为项目是部署到k8s中,当本地项目启动没问题时,业务方的研发就一直把问题聚焦在k8s中,一直觉得是k8s引发的问题。

后面他们业务方找到我,叫我帮忙排查,我第一反应就是可能打包出了问题,于是我让业务方打个包,本地以java -jar试下,但业务方的研发又很肯定的说,他试过本地打jar运行也没问题。因为业务方的代码我这边是没权限访问的,没办法进行验证。后面只能建议他们安装arthas,最终结合arthas解决了问题

点赞
收藏
评论区
推荐文章
橘子橙 橘子橙
4年前
vue-element-admin项目打包后,iconfont图标出现乱码
使用vueelementadmin或者vueelementtemplate开发的项目,打包到线上,就出现了图标乱码,f12后能看到icon元素为.eliconclose:before{content:"□"}的情况(如下)
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Stella981 Stella981
3年前
Kerberos无约束委派的攻击和防御
 0x00前言简介当ActiveDirectory首次与Windows2000Server一起发布时,Microsoft就提供了一种简单的机制来支持用户通过Kerberos对Web服务器进行身份验证并需要授权用户更新后端数据库服务器上的记录的方案。这通常被称为Kerberosdoublehopissue(双跃点问题),
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
3年前
OpenJDK11与Spring Cloud Finchley的不兼容问题与解决
本文的环境:OpenJDK11.0.4,SpringCloudfinchleySR4,SpringBoot2.0.3最近遇到了一个问题,在feign调用的时候,时常会出现这样一个奇怪的错误:2019100708:00:00.620ERRORxxx,e1ba4c7540954aa3,871b99c4576d42e3
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
3年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin
Stella981 Stella981
3年前
Hibernate纯sql查询结果和该sql在数据库直接查询结果不一致
问题:今天在做一个查询的时候发现一个问题,我先在数据库实现了我需要的sql,然后我在代码中代码:selectdistinctd.id,d.name,COALESCE(c.count_num,0),COALESCE(c.count_fix,0),COALESCE(c
Easter79 Easter79
3年前
SwiftUI 跨组件数据传递
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言众所周知,SwiftUI的开发模式与React、Flutter非常相似,即都是声明式UI,由数据驱动(产生)视图,视图也会与数据自动保持同步,框架层会帮你处理“绑定”的问题。在声明式UI中不存在命令式地让一个视图变成xxx
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这