SpringBoot突報java.lang.NoSuchFieldError分析


 SpringBoot項目,引了一個內部的工具包,竟然導致啟動失敗,報找不到freemarker Configuration類的一個屬性,網上的解法都大同小異,最終用了自己的辦法解決,花點時間記錄下來,希望能幫助到別人。

關鍵詞:SpringBoot,AutoConfiguration,freemarker,NoSuchFiledError

問題背景

最近在開發過程中由於要用到公司內部的一個工具類,所以添加了common-util.jar到工程的maven依賴中,功能開發完了,打算本地調試一下,但悲劇的是應用啟動失敗了。查看堆棧信息,並沒有我新添加的代碼,是Spring初始化失敗了,堆棧信息如下:

 

 

 

初步分析

對照着堆棧來看,錯誤很明確,就是在運行時缺少freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS 這個field導致,但是怎么解決呢?度娘上基本都是下面這兩種解決辦法:

 

 

 圖片來源於https://www.cnblogs.com/geekdc/p/9243069.html

我並沒有采用這兩種辦法,因為這並不是一個新搭建的工程,而且我這次也沒有做過任何版本升級,唯一的可能就是引入的新jar包有問題。

 

再看異常堆棧

從堆棧來看是缺少了一個Field導致,那什么情況下會造成這種情況呢,我第一時間猜測應該是jar包沖突了,因為之前遇到過一次,有興趣的可以看這里,我查看了新引入jar包內部的pom文件,確實是依賴了freemarker(2.3.16)的jar包,而且scope是compile,就這樣,這個低版本的freemarker被傳遞依賴進來了,至於最終使用哪個,就由maven的仲裁機制決定了,很明顯我這個工程最終用了老版本,所以導致運行時找不到Field,解決方案很簡單,使用exclusion將老版本的freemarker排除就好了。一般故事到這兒就結束了,但是我這個人手賤,偏要看看項目以前依賴的freemarker是什么版本的,結果看完又不能按時睡覺了(題外話:白天老是這事那事,晚上才有時間寫代碼)。

意外收獲

我使用mvn dependency:tree將所有依賴都輸出到文本文件里,然后搜索freemarker,結果意外的發現居然沒有依賴freemarker,這是什么情況,剛剛不是還j說ar包沖突了嗎,難道是自我沖突了?

 

一時間沒有了頭緒,想百度都不知道關鍵字寫什么。一般這個時候我會點根煙開始復盤,從頭到尾反復看案發現場,總能發現蛛絲馬跡。終於靈光一下,我腦子里閃過這么一段話“之前沒有freemarker.jar的時候不報錯,現在有了也應該不報錯才對,除非SpringBoot具有自動發現功能,可以實現組件可插拔”,就這樣,我嘗試搜索“SpringBoot自動發現”,看看百度給我的結果:

 

 

 

 我很自然的過濾了第二條搜索內容,因為我知道那不是我想要的,我內心想表述的其實是“自動轉配、自動配置”。

根因分析

了解了自動裝配的大概原理后,我梳理出了這次啟動失敗的根本原因,如下:

  1. SpringBoot啟動的時候觸發自動配置;
  2. spring-boot-autoconfigure-1.5.11.RELEASE.jar\META-INF\spring.factories里面是需要自動配置的Bean,里面就有FreeMarkerAutoConfiguration;
  3. 自動配置一般會有一些約束條件,滿足條件才會自動配置,條件通過@Condition***這類注解來聲明;
  4. FreeMarkerAutoConfiguration的自動配置約束條件為@ConditionalOnClass({freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class}),意味着只有freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class這兩個類都存在的時候才會自動配置;
  5. 如果第四步滿足條件,最終會觸發FreeMarkerConfigurationFactory.newConfiguration這個方法,這個方法內部用到了Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS這個Field,它來源於freemarker.jar,就是堆棧中報NoSuchFiledError的主角;
  6. 如果第四步不滿足條件,就不觸發FreeMarkerAutoConfiguration的自動配置,進而不會用到Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS這個Field;

FreeMarkerConfigurationFactory來源於spring-context-support-4.3.15.RELEASE.jar,這個jar包之前就依賴了,而Configuration來源於freemarker.jar,是common-util.jar傳遞依賴進來的,所以正好滿足了上面的第四步,進而觸發了第五步,最終導致SpringBoot啟動失敗。

匯總解決辦法

通過上面的一些分析,我認為這個問題一共有以下幾種辦法:

  1. 如果你的項目中沒有用到freemarker的話可以禁用freemarker的自動配置,像初步分析那里提到的;
  2. 升級freemarker到高版本;
  3. 像我遇到的問題一樣,確實不需要freemarker功能,可以將freemarker.jar排除;

總結

雖然是個很小的問題,但是解決過程確實讓我收獲不少,之前對於SpringBoot只停留在簡單使用階段,通過這次排查過程,也算是了解了SpringBoot自動配置的大體思想,這種可插拔的設計真的是非常巧妙。

 

如果覺得有用,請您點個推薦。

參考資料:

https://www.cnblogs.com/nijunyang/p/12051770.html

https://blog.csdn.net/ZYC88888/article/details/84245127

 

 

來我的公眾號與我交流

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM