SpringBoot項目jar、war包啟動解析
一、jar包和war包的區(qū)別
1.1 war包
war包是Java Web應(yīng)用程序的一種打包方式符合Servlet標(biāo)準(zhǔn),它是Web Archive的縮寫,主要用于存儲Web應(yīng)用程序相關(guān)的文件,包括Java類文件、JSP、HTML、CSS、JavaScript、圖片等資源文件。
war包需要部署到web服務(wù)器中(Tomcat、Apache、IIS)
1.2 jar包
jar包是類的歸檔文件,主要用于存儲Java類文件和相關(guān)資源文件。它通常被用于封裝Java應(yīng)用程序或Java類庫,方便程序的部署和發(fā)布
jar包可以被JVM直接加載和運行。
1.3 主要區(qū)別:
jar包主要用于存儲Java類文件和相關(guān)資源文件,而war包主要用于存儲Web應(yīng)用程序相關(guān)的文件。
jar包可以被JVM直接加載和運行,而war包需要被Web服務(wù)器加載和運行。
jar包通常用于封裝Java應(yīng)用程序或Java類庫,而war包用于封裝Java Web應(yīng)用程序。
二、SpringBoot使用war包啟動
war包啟動:需要先啟動外部的Web服務(wù)器,實現(xiàn)Servlet3.0規(guī)范中引導(dǎo)應(yīng)用啟動類,然后將war包放入Web服務(wù)器下,Web服務(wù)器通過回調(diào)引導(dǎo)應(yīng)用啟動類方法啟動應(yīng)用。
2.1 Servlet3.0規(guī)范中引導(dǎo)應(yīng)用啟動的說明
在Servlet容器(Tomcat、Jetty等)啟動應(yīng)用時,會掃描應(yīng)用jar包中 ServletContainerInitializer 的實現(xiàn)類。
框架必須在jar包的 META-INF/services 的文件夾中提供一個名為 javax.servlet.ServletContainerInitializer 的文件,文件內(nèi)容要寫明 ServletContainerInitializer 的實現(xiàn)類的全限定名。
這個 ServletContainerInitializer 是一個接口,實現(xiàn)它的類必須實現(xiàn)一個方法:onStartUp
可以在這個 ServletContainerInitializer 的實現(xiàn)類上標(biāo)注 @HandlesTypes 注解,在應(yīng)用啟動的時候自行加載一些附加的類,這些類會以字節(jié)碼的集合形式傳入 onStartup 方法的第一個參數(shù)中。
2.2 SpringBootServletInitializer的作用和原理
Spirng中
SpringServletContainerInitializer實現(xiàn)了Servlet的規(guī)范
.class)public class SpringServletContainerInitializer implements ServletContainerInitializer { ? ?
? ?public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
? ? ? ? ? ?throws ServletException { ? ? ? ?// SpringServletContainerInitializer會加載所有的WebApplicationInitializer類型的普通實現(xiàn)類
? ? ? ?List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); ? ? ? ?if (webAppInitializerClasses != null) { ? ? ? ? ? ?for (Class<?> waiClass : webAppInitializerClasses) { ? ? ? ? ? ? ? ?// 如果不是接口,不是抽象類
? ? ? ? ? ? ? ?if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
? ? ? ? ? ? ? ? ? ? ? ?WebApplicationInitializer.class.isAssignableFrom(waiClass)) { ? ? ? ? ? ? ? ? ? ?try { ? ? ? ? ? ? ? ? ? ? ? ?// 創(chuàng)建該類的實例
? ? ? ? ? ? ? ? ? ? ? ?initializers.add((WebApplicationInitializer) waiClass.newInstance());
? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ?catch (Throwable ex) { ? ? ? ? ? ? ? ? ? ? ? ?throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?}
? ? ? ? ? ?}
? ? ? ?} ? ? ? ?if (initializers.isEmpty()) {
? ? ? ? ? ?servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); ? ? ? ? ? ?return;
? ? ? ?}
? ? ? ?servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
? ? ? ?AnnotationAwareOrderComparator.sort(initializers); ? ? ? ?// 啟動Web應(yīng)用onStartup方法
? ? ? ?for (WebApplicationInitializer initializer : initializers) {
? ? ? ? ? ?initializer.onStartup(servletContext);
? ? ? ?}
? ?}
}
(WebApplicationInitializer
@HandlesTypes使用BCEL的ClassParser在字節(jié)碼層面讀取了/WEB-INF/classes和jar中class文件的超類名和實現(xiàn)的接口名,判斷是否與記錄的注解類名相同,若相同再通過
org.apache.catalina.util.Introspection類加載為Class對象保存起來,最后傳入onStartup方法參數(shù)中
SpringServletContainerInitializer類上標(biāo)注了@HandlesTypes(WebApplicationInitializer.class),所以會導(dǎo)入WebApplicationInitializer實現(xiàn)類
SpringBoot中
SpringBootServletInitializer是WebApplicationInitializer的抽象類,實現(xiàn)了onStartup方法
創(chuàng)建父容器
所以我們只需要自定義類繼承
SpringBootServletInitializer并實現(xiàn)configure方法告訴啟動類所在的位置就可以實現(xiàn)SpringBoot自啟動了
例如:
三、SpringBoot使用jar包啟動
按照java官方文檔規(guī)定,java -jar命令引導(dǎo)的具體啟動類必須配置在MANIFEST.MF中的Main-class屬性中,該值代表應(yīng)用程序執(zhí)行入口類也就是包含main方法的類。
從MANIFEST.MF文件內(nèi)容可以看到,Main-Class這個屬性定義了
org.springframework.boot.loader.JarLauncher,JarLauncher就是對應(yīng)Jar文件的啟動器。而我們項目的啟動類SpringBootDemoApplication定義在Start-Class屬性中,
JarLauncher會將BOOT-INF/classes下的類文件和BOOT-INF/lib下依賴的jar加入到classpath下,然后調(diào)用META-INF/MANIFEST.MF文件Start-Class屬性完成應(yīng)用程序的啟動。
關(guān)于 jar?官方標(biāo)準(zhǔn)說明請移步
JAR File Specification JAR (file format)
SpringBoot的jar包,會有3個文件夾:
BOOT-INF:存放自己編寫并編譯好的 .class 文件和靜態(tài)資源文件、配置文件等
META-INF:有一個 MANIFEST.MF 的文件
org:spring-boot-loader 的一些 .class 文件

META-INF 下面的 MANIFEST.MF 文件,里面的內(nèi)容如下:
在 Start-Class 中注明了 SpringBoot 的主啟動類
在 Main-Class 中注明了一個類: JarLauncher
父類Launcher#launch
3.1 registerUrlProtocolHandler:注冊URL協(xié)議并清除應(yīng)用緩存
先設(shè)置當(dāng)前系統(tǒng)的一個變量
java.protocol.handler.pkgs,而這個變量的作用,是設(shè)置 URLStreamHandler 實現(xiàn)類的包路徑。
之后要重置緩存,目的是清除之前啟動的殘留。
3.2createClassLoader:設(shè)置類加載路徑
3.3 執(zhí)行main方法
所以 SpringBoot 應(yīng)用在開發(fā)期間只需要寫 main 方法,引導(dǎo)啟動即可。