1. 程式人生 > 其它 >Spring 熱部署造成的類載入問題

Spring 熱部署造成的類載入問題

如果你的專案在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