如果你的項目在IDE中出現了像下面這些奇怪的錯誤
-
object is not an instance of declaring class // 對象不是聲明類的實例
-
java.lang.ClassCastException: com.example.A cannot be cast to com.example.A // A類無法轉換成A類
-
又或者是全局靜態變量莫名變為了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
進行加載。造成本該是一個類的類,因被不同的類加載器加載而同時出現兩個不同的類,而出現上面的錯誤。
解決辦法:
-
不使用
spring-boot-devtools
-
在resource下創建META-INF/spring-devtools.properties
restart.exclude.companycommonlibs=排除的jar包 restart.include.projectcommon=包含的jar包 // 使用restartClassLoader加載
下面是我解決這個問題的實際步驟
下面是我工作中遇到這個問題的解決思路。我的項目中出現了1和3這樣的問題,我的項目中有一個類
AppContext
類,其中有一個全局靜態變量context
-
使用 debug 斷點,在 context 被賦初始值的的地方的下一行,暫停程序,使用
jmap -dump:live,format=b,file=heap-dump-1.bin pid
導出程序的堆文件,在使用jhat heap-dump.bin
分析堆文件,此時 context 不為null -
程序繼續運行,再次導堆文件進行分析,這時 context 變為了 null ,同時發現 ClassLoader 發生了變化
-
基本確認是因為ClassLoader變化導致的問題,再次使用
java -jar xx.jar
運行分析,感覺 RestartClassLoader 有些特殊,然后百度搜索 restartClassLoader 解決了問題
參考資料: