SpringBoot之class is not visible from class loader

本文分析了SpringBoot应用中因类加载器问题导致的TestServiceisnotvisiblefromclassloader异常。详细介绍了异常出现的原因,包括特定条件下代理类与接口类被不同类加载器加载而导致的冲突,并提供了两种可行的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

最近在搭建SpringBoot的新应用,遇到个有意思的问题,如题就是在加载某一个类时候抛出了class is not visible from class loader, 下面就带大家看看是如何产生的。

二、问题产生

  • 首先有如下bean的定义:

0?wx_fmt=png

如上代码代理类TestProxy继承了TestService类,并且在init方法里面消费了接口TestService的提供的远程服务。RemoteConsumerProxy类做了两件事,首先是生成接口TestService的远程服务bean这里假设为beanRemote,然后对beanRemote进行JDK代理生成代理类beanRemoteProxy,代理的作用是执行具体远程服务方法前进行统一限流处理或者指定调用的ip等。并且RemoteConsumerProxy是通过二方库方式引入。

  • 然后引入了SpringBoot的开发工具模块spring-boot-devtools。

  • 满足上面两个条件后注入TestProxy到IOC容器,运行Spring-boot工程的main函数(注意打成jar,然后运行jar则不会有这个问题),就会抛出:TestService is not visible from class loader

从调用堆栈看是java.lang.reflect.Proxy的apply方法抛出的异常。

0?wx_fmt=png

image.png

三、问题分析

既然是Proxy的apply方法抛出了异常,那么就看什么情况下会抛出异常,从Proxy的代码看是 interfaceClass != intf时候抛出异常。

这里intf是通过 RemoteConsumerProxy<TestService> proxy = RemoteConsumerProxy() .setInterfaceClass(TestService.class)传递的,TestService.class

而interfaceClass则是使用

0?wx_fmt=png

创建的,intf.getName获取的就是TestService带包路径的类名

到这里对类加载器比较熟悉的童鞋应该会有所思了,同一个类两次加载后的Class对象不一样,那只有一种情况,那就是使用了两个类加载器加载了同一个类。

为了证明这个,可以在init方法里面添加如下代码:

0?wx_fmt=png

运行后输出为:

0?wx_fmt=png

  • 从结果可知TestProxy和TestService是使用RestartClassLoader类加载器加载的,所以调用代码 RemoteConsumerProxy.setInterfaceClass(TestService)传递的时候传递的是RestartClassLoader加载的Class对象。

  • 从结果可知RemoteConsumerProxy.setInterfaceClass是AppClassLoader加载的,所以ACCSHSFConsumerProxy内部执行代码

0?wx_fmt=png

时候也是使用AppClassLoader加载的,也就是这里的TestService是AppClassLoader加载的,所以同一个接口由两个类加载器加载,所以两个Class对象不相等。

另外通过debug可以发现RestartClassLoader的父加载器就是AppClassLoader。

那么RestartClassLoader又是什么那?从何而来?

总结:在IDE里面main函数方式运行时候由于会编译类,classpath下的内容会发生变化,所以会触发restart,从而导致抛出异常。而首先通过mvn clean package 打包,然后在java -jar jar方式由于jar内部不会变了所以不会触发restart,所以运行正常。

四、如何解决

  • 方案一,排查掉spring-boot-devtools模块模块的maven引入可以解决,这时候所有类都是使用APPClassloader加载。

  • 方案二,可以引入spring-boot-devtools模块,但是禁用禁用reStart功能

0?wx_fmt=png

五、总结

虽然是同一个类,但是使用不同的类加载器加载后得到的Class对象是不一样的,区分一个Class对象是否相等要看包名+类名,也要看是否是同一个类加载器。另外SpringBoot的spring-boot-devtools模块的restart功能在IDE里面运行main函数时候应该有bug。欢迎大家批评指正。

六、 参考

  • https://docs.spring.io/sp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值