在這篇文章中我想分享我最近遇到的一個問題,我想您可能會感興趣。
出了什麼問題?
測試環境和生產環境的源代碼是相同的,本地和測試環境都運作良好,只有生產環境運行的服務類的NPE加載失敗,這是為了新的需求而添加的。此類別繼承自Customize包(自研工具包)的介面。
專案結構
Customize 的設計依賴 Spring 來管理 API 和服務。透過Spring自動掃描,初始化類別時會載入ApiEnhancer和ServiceEnhancer的公共類,並透過ApplicationContext取得實例。當調試發現載入它們時,ApplicationContext為null,還沒有初始化。是在setApplicationContext方法中透過實作ApplicationContextAware來初始化的,所以推測setApplicationContext方法還沒有執行。
在ApplicationContextProvider類別的載入過程中,靜態方法在初始化階段被載入到方法區。然而,當使用ApplicationContext取得bean的實例時,靜態方法是直接透過類別名稱呼叫的。只要API是在ApplicationContextProvider在堆中分配記憶體並實例化之前創建的,就不會呼叫setApplicationContext方法進行初始化。
applicationContextProvider使用註解@Component,Api使用註解@RestController,兩個類別在同一個路徑下。然而,當 Spring 掃描並載入它們時,沒有特定的順序,這意味著這兩個類別中的每一個都可能在另一個類別之前創建。在生產環境中,Api是先於applicationContextProvider創建的,導致透過類別名稱直接呼叫靜態方法取得bean時,applicationContext沒有被初始化。
解
如果沒有執行ApplicationContextAware介面的實作類別的setApplicationContext方法,首先檢查實作類別是否設定了延遲載入或專案是否配置了全域延遲載入。
在這個專案中,問題是由ApiEnhancer和ApplicationContextAware介面的實作類別的建立順序引起的,而Api類別只是一個常規的RESTful API,處理業務邏輯,可以在前端呼叫時載入-結束頁。因此,改為延遲載入解決了問題。
建議在載入順序方面盡量減少類別之間的依賴關係。如果不可避免,可以使用延遲加載,或使用@DependsOn、@Order、@Priority等註解來控制bean的加載順序
為什麼生產環境和測試環境的建立順序不同?
我們來調試一下Spring的掃描過程。
從ClassPathBeanDeterminationScanner的scan方法開始。
doScan方法
ClassPathScanningCandidateComponentProvider 的 scanCandidateComponents 方法
PathMatchingResourcePatternResolver 的 findAllClassPathResources 方法
doFindPathMatchingJarResources 方法
JarFile是Java標準庫中java.util.jar套件下的一個類,它繼承並擴展了ZipFile。 jarFile.entries() 傳回一個名為 JarEntryIterator 的迭代器。
JarExitIterator 是 迭代器模式 的實現,它迭代 JarFile 父類別的條目。
ZipFile 的 Entry 方法傳回一個名為 ZipExitIterator 的迭代器。
ZipExitIterator 的 nextElement 方法呼叫 next 方法,後者呼叫 getNextEntry 方法。
getNextEntry 是一個原生方法。
native 方法是用非 Java 語言實現的,並在 Java 虛擬機器內呼叫來實現底層功能,這可能會因環境(作業系統或 JDK 版本)而異。 JAR包本身沒有順序,所以實際的遍歷順序可能會根據不同的JAR打包工具和環境而有所不同。
以上是applicationContextAware介面的setApplicationContext方法執行問題,取得Spring bean失敗的詳細內容。更多資訊請關注PHP中文網其他相關文章!