springBoot項目 war包啟動原理
參考鏈接:
【spring boot war包啟動原理】 https://www.cnblogs.com/stone-with-big-ears/p/10950581.html 【Tomcat war包加載過程】https://www.jianshu.com/p/6e435a5a4fee
spring boot war啟動是利用Servlet 3.0新增的ServletContainerInitializer接口結合SPI(Service Provider Interface)機制實現的。
springBoot的application.java是應用程序啟動入口,它的父類,org.springframework.boot.web.servlet.support.SpringBootServletInitializer
public abstract class SpringBootServletInitializer implements WebApplicationInitializer
springBoot使用war的啟動方式原理實現:
通過spring-web-xxx.jar包的org.springframework.web.SpringServletContainerInitializer類實現ServletContainerInitializer接口,從而達到無web.xml配置的啟動
主要邏輯
Spring在spring-web-version.jar/META-INF/services/javax.servlet.ServletContainerInitializer文件中, 配置了spring對ServletContainerInitializer接口的實現類org.springframework.web.SpringServletContainerInitializer。 Servlet Container啟動階段掃描jar包中META-INF/services/javax.servlet.ServletContainerInitializer文件, 獲取ServletContainerInitializer實現類並實例化,解析ServletContainerInitializer上@HandlesTypes注解, 查找出@HandlesTypes限定的類型集合,作為ServletContainerInitializer.onStartup方法處理的第一個參數c。 Servlet Container依次調用每個ServletContainerInitializer實例的onStartup。war包啟動的場景中會調用SpringServletContainerInitializer.onStartup方法, 該方法循環調用c集合中每個 WebApplicationInitializer子類(即SpringBootServletInitializer)的onStartup方法。 SpringBootServletInitializer.onStartup方法調用SpringBootServletInitializer.createRootApplicationContext方法, createRootApplicationContext方法中構建SpringApplication並執行SpringApplication.run方法以啟動整個spring項目。
tomcat啟動主要的相關類,啟動順序從上到下
BootStrap Catalina StandardServer StandardService // 從這里開始就是屬於Container 容器范疇 StandardEngine StandardHost StandardContext StandardWrapper
StandardEngine 表示servlet引擎容器
StandardHost 表示虛擬host容器
StandardContext 表示servlet上下文,一個獨立的web應用程序
從StandardContext開始
因為engine,host,context,StandardWrapper都繼承自ContainerBase類,
ContainerBase extends LifecycleMBeanBase
LifecycleMBeanBase extends LifecycleBase
所以,這些container類都實現了Lifecycle接口的監聽器
在StandardContext中,它的監聽器類是:ContextConfig
解析應用程序的操作都是在監聽器中完成了
ContextConfig.java
ContextConfig.java public void lifecycleEvent(LifecycleEvent event) { if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { configureStart(); } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) { beforeStart(); ... }
ContextConfig.configureStart(){
。。。
webConfig()
。。。
}
在webConfig()方法
對應用程序掃描web.xml文件
處理為其他所有內容之后添加的web片段,因此其他所有內容均具有優先權
將Servlet標記為可覆蓋,SCI配置可以替換默認配置
webConfig(){ 。。。 // Parse context level web.xml InputSource contextWebXml = getContextWebXmlSource(); if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) { ok = false; } 。。。 }
web配置步驟
servlet context級別的web.xml解析 如果沒有找到應用程序的/WEB-INF/web.xml文件,默認會設置標志位ok=true,用來后續的ServletContainerInitializer實現類處理 1.掃描應用程序/WEB-INF/lib路徑下的JAR,未找到/META-INF/web-fragment.xml文件的jar包添加到生成的Map中。 如果任何應用程序JAR都具有 web-fragment.xml文件,則將在此時進行解析。 web-fragment.xml容器提供的JAR文件將被忽略。 Servlet3.0 WebFragment掃描 2.確定web-fragments這些片段的啟動順序 3.查找所有ServletContainerInitializer實現類, 處理ServletContainerInitializers的實現類,這也是servlet 3.0新增的特性, 容器在啟動時使用 JAR 服務 API(JAR Service API) 來發現 ServletContainerInitializer 的實現類, 並且容器將 WEB-INF/lib 目錄下 JAR 包中的類都交給該類的 onStartup() 方法處理, 我們通常需要在該實現類上使用 @HandlesTypes 注解來指定希望被處理的類, 過濾掉不希望給 onStartup() 處理的類。在onStartup方法中可以優先加載這些類, 或者修改其中的方法等。 這步主要是把這些類找到放到Set<ServletContainerInitializer> scis中; 4.將應用中的web.xml與orderedFragments進行合並,合並在WebXml類的merge方法中實現 5.將應用中的web.xml與全局的web.xml文件(conf/web.xml和web.xml.default)進行合並 6.用合並好的WebXml來配置Context,這一步在處理servlet時,會為每個servlet創建一個wrapper, 並調用addChild將每個wrapper作為context子容器,后續分析