文章插圖

文章插圖
Springboot工程war包模式啟動流程
出于項目部署的需要,目前做的一個項目需要通過war包進行部署,那么就帶來一個問題,就是在原本的main方法中進行的一些邏輯會失效,以至于出現一些不可預知的錯誤#1 。在這里加載了一個classpath下的一個json文件,將其加載到系統屬性中,但是在后續運行時出現NullPointException 。很納悶,我這本地運行的好好的,咋發布到環境上就出問題了,后面一翻源碼才明白,原來我們本地運行是java -jar模式運行的,此時虛擬機運行的是工程的main方法 。但是發布到環境后,是以war包運行的,此時將不會執行本工程的main方法,而是由tomcat的main方法進行啟動 。這個時候問題問題就來了,數據沒加載到全局系統變量中 。1#
在查閱了spring-web這個依賴后,在META-INF下有services的文件夾,javax.servlet.ServletContainerInitializer,這個東西具體是干什么的呢?其實就是在tomcat啟動后,通過JDK的SPI調用實現了文件里面的類,這里spring使用了org.springframework.web.SpringServletContainerInitializer,打開這個類會發現class上有個HandlesTypes的注解,那么這個里面修飾的class是做什么用的呢,主要作為onStartup方法的入參使用——webAppInitializerClasses會收集所有實現了這個接口的類,循環調用他們的onStartup方法 。#2
#3
然后我們的啟動類org.springframework.boot.web.servlet.support.SpringBootServletInitializer剛好又是它的子類,那么會調用到它 。這里采用了模板設計模式 。org.springframework.boot.web.servlet.support.SpringBootServletInitializer#configure這個方法作為org.springframework.boot.web.servlet.support.SpringBootServletInitializer#createRootApplicationContext的一部分,作為鉤子方法影響主流程 。構建出一個SpringApplication對象后執行它的run方法,這里就是最常規的Springboot工程啟動了
#4
【tomcat部署war包步驟 tomcat部署war包】最后的解決方案是通過訂閱spring的容器刷新事件來完成靜態數據的加載#5,因為無論是jar包啟動,還是war包啟動,都是要調用spring的核心方法——refresh,當刷新完成后,會拋出一個ContextRefreshedEvent事件,所有訂閱這個事件的bean都會收到,在這里進行部分操作完成一些數據加載工作#5