spring-boot-devtools 热部署造成的类加载问题

发布时间 2023-03-29 13:54:17作者: 学无止境,贵在当下

如果你的项目在IDE中出现了像下面这些奇怪的错误

  1. object is not an instance of declaring class
    // 对象不是声明类的实例
    
    
  2. java.lang.ClassCastException: com.example.A cannot be cast to com.example.A
    // A类无法转换成A类
    
    
  3. 又或者是全局静态变量莫名变为了null,前一秒才看到静态变量被赋值了,下一秒获取的时候就出现了空指针异常。

而且这些错误在使用 java -jar xx.jar 还不会出现,那么很有可能是因为你是用了 spring-boot-devtools 依赖

官方描述:

By default, any open project in your IDE will be loaded using the “restart” classloader, and any regular .jar file will be loaded using the “base” classloader. If you work on a multi-module project, and not each module is imported into your IDE, you may need to customize things. To do this you can create a META-INF/spring-devtools.properties file.

默认情况下,IDE中任何打开的项目都将使用 restart classloader加载,而任何常规的.jar文件都将使用 base classloader加载。如果你在一个多模块项目上工作,并且不是每个模块都被导入到你的IDE中,你可能需要自定义一些东西。要做到这一点,你可以创建一个META-INF/spring-devtools.properties文件。

原因

  • 项目中的java文件,因其可能随时被修改,为了热部署及时生效,这些java文件对于的类会使用 org.springframework.boot.devtools.restart.classloader.RestartClassLoader 类加载器进行加载。如果同时有jar中的一些代码使用了反射等技术使用我们项目的类时,就会使用 AppClassLoade 进行加载。造成本该是一个类的类,因被不同的类加载器加载而同时出现两个不同的类,而出现上面的错误。

解决办法

  1. 不使用 spring-boot-devtools

  2. 在resource下创建META-INF/spring-devtools.properties

    restart.exclude.companycommonlibs=排除的jar包
    restart.include.projectcommon=包含的jar包  // 使用restartClassLoader加载
    
    

下面是我解决这个问题的实际步骤

下面是我工作中遇到这个问题的解决思路。我的项目中出现了1和3这样的问题,我的项目中有一个类 AppContext 类,其中有一个全局静态变量 context

  1. 使用 debug 断点,在 context 被赋初始值的的地方的下一行,暂停程序,使用 jmap -dump:live,format=b,file=heap-dump-1.bin pid 导出程序的堆文件,在使用 jhat heap-dump.bin 分析堆文件,此时 context 不为null

  2. 程序继续运行,再次导堆文件进行分析,这时 context 变为了 null ,同时发现 ClassLoader 发生了变化

  3. 基本确认是因为ClassLoader变化导致的问题,再次使用 java -jar xx.jar 运行分析,感觉 RestartClassLoader 有些特殊,然后百度搜索 restartClassLoader 解决了问题

参考资料

https://my.oschina.net/heartarea/blog/788294

本文转自 https://www.cnblogs.com/zerolinck/p/15412795.html,如有侵权,请联系删除。