1. 問題背景
某應用在啟動完提供JSF服務後,短時間內出現了大量的空指標異常。
分析日誌,發現是服務依賴的藏經閣配置資料未載入完成導致。即所謂的有損上線或者是直接釋出,當應用啟動時,service還沒載入完,就開始對外提供服務,導致失敗呼叫。
關鍵程式碼如下
資料的初始化載入是透過實現CommandLineRunner介面完成的
@Component
public class LoadSystemArgsListener implements CommandLineRunner {
@Resource
private CacheLoader cjgConfigCacheLoader;
@Override
public void run(String... args) {
// 載入藏經閣配置
cjgConfigCacheLoader.refresh();
}
}
cjgConfigCacheLoader.refresh()方法內部會將資料載入到記憶體中
/** 藏經閣配置資料 key:租戶 value:配置資料 */
public static Map<String, CjgRuleConfig> cjgRuleConfigMap = new HashMap<>();
如果此時還未載入完資料,呼叫cjgRuleConfigMap.get("301").getXX(),則會報空指標異常
總結根因:JSF Provider釋出早於服務依賴的初始化資料載入,導致失敗呼叫
2. 問題解決
在解決此問題前,我們需要先回憶並熟悉下Spring Boot的啟動過程、JSF服務的釋出過程
1)Spring Boot的啟動過程(版本2.0.7.RELEASE)
run方法,主要關注refreshContext(context)重新整理上下文
public ConfigurableApplicationContext run(String... args) {
// 建立 StopWatch 例項:用於計算啟動時間
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 獲取SpringApplicationRunListeners:這些監聽器會在啟動過程的各個階段傳送對應的事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 建立並配置Environment:包括準備好對應的`Environment`,以及將`application.properties`或`application.yml`中的配置項載入到`Environment`中
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
// 列印Banner:如果 spring.main.banner-mode 不為 off,則列印 banner
Banner printedBanner = printBanner(environment