- 类名完全相同,但是类加载器不同
- 明明代码没改动,线上没问题,拉下来开发就报错??
问题分析
- JVM判断两个类对象是否相同的依据:一是类全称;一个是类加载器.
- 大家都知道虚拟机的默认类加载机制是通过双亲委派实现的。springboot为了实现程序动态性(比如:代码热替换、模块热部署等,白话讲就是类文件修改后容器不重启),“破坏或牺牲” 了双亲委派模型。springboot通过强行干预-- “截获”了用户自定义类的加载(由jvm的加载器AppClassLoader变为springboot自定义的加载器RestartClassLoader,一旦发现类路径下有文件的修改,springboot中的spring-boot-devtools模块会立马丢弃原来的类文件及类加载器,重新生成新的类加载器来加载新的类文件,从而实现热部署。比较流行的OSGI也能实现热部署)。
JVM判断两个类对象是否相同的依据:一是类全称;一个是类加载器.
大家都知道虚拟机的默认类加载机制是通过双亲委派实现的。springboot为了实现程序动态性(比如:代码热替换、模
块热部署等,白话讲就是类文件修改后容器不重启),“破坏或牺牲”了双亲委派模型。springboot通过强行干预-- “截获”
了用户自定义类的加载(由jvm的加载器AppClassLoader变为springboot自定义的加载器RestartClassLoader, 一旦发
现类路径下有文件的修改,springboot中的spring-boot-devtools模块会立马丢弃原来的类文件及类加载器, 重新生成新
的类加载器来加载新的类文件,从而实现热部署。比较流行的OSGI也能实现热部署)。
问题解决
关掉热部署即可
1 spring-boot-devtools会检测类路径的变化,当类路径内容发生变化后会自动重启应用程序。Spring Boot的重启技术通过使用两个类加载器。由于使用的是双类加载机制重启会非常快,如果启动较慢也可使用JRebel重加载技术。
(1)base classloader (Base类加载器):加载不改变的Class,如第三方提供的jar包。
(2)restart classloader(Restart类加载器):加载正在开发的Class。
到这里相信大家知道了,为什么重启很快,因为重启的时候只是加载了在开发的Class,没有重新加载第三方的jar包。
2 因为类文件的修改(一般在IDE环境下修改)只会发生在开发阶段,所以就解释了ClassCastException为什么总是发生在开发阶段,而测试或生产环境却运行正常。
3 针对上面问题我们也能自然想到以后只要是在反序列化及热部署的场景下出现的类型转换异常基本上就是这个问题了。