最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot應(yīng)用jar包啟動(dòng)原理詳解

 更新時(shí)間:2022年03月31日 15:26:25   作者:禿頭愛健身  
本文主要介紹了SpringBoot應(yīng)用jar包啟動(dòng)原理詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1、maven打包

Spring Boot項(xiàng)目的pom.xml文件中默認(rèn)使用spring-boot-maven-plugin插件進(jìn)行打包:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

在執(zhí)行完maven clean package之后,會(huì)生成來個(gè)jar相關(guān)文件:

  • test-0.0.1-SNAPSHOT.jar
  • test-0.0.1-SNAPSHOT.jar.original

2、Jar包目錄結(jié)構(gòu)

以筆者的test-0.0.1-SNAPSHOT.jar為例,來看一下jar的目錄結(jié)構(gòu),其中都包含哪些目錄和文件?

請(qǐng)?zhí)砑訄D片描述

可以概述為:

spring-boot-learn-0.0.1-SNAPSHOT
├── META-INF
│ └── MANIFEST.MF
├── BOOT-INF
│ ├── classes
│ │ └── 應(yīng)用程序
│ └── lib
│ └── 第三方依賴jar
└── org
└── springframework
└── boot
└── loader
└── springboot啟動(dòng)程序

其中主要包括三大目錄:META-INF、BOOT-INF、org。

1)META-INF內(nèi)容

META-INF記錄了相關(guān)jar包的基礎(chǔ)信息,包括:入口程序。具體內(nèi)容如下:

Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: tms-start
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.saint.StartApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.4.5
Created-By: Maven Jar Plugin 3.2.0
Main-Class: org.springframework.boot.loader.JarLauncher

  • Main-Classorg.springframework.boot.loader.JarLauncher,即jar啟動(dòng)的Main函數(shù);
  • Start-Classcom.saint.StartApplication,即我們自己SpringBoot項(xiàng)目的啟動(dòng)類;也是下文提到的項(xiàng)目的引導(dǎo)類。

2)BOOT-INF內(nèi)容

  • BOOT-INF/classes目錄:存放應(yīng)用編譯后的class文件源碼;
  • BOOT-INF/lib目錄:存放應(yīng)用依賴的所有三方j(luò)ar包文件;

3)org內(nèi)容

org目錄下存放著所有SpringBoot相關(guān)的class文件,比如:JarLauncher、LaunchedURLClassLoader。

請(qǐng)?zhí)砑訄D片描述

3、可執(zhí)行Jar(JarLauncher)

從jar包內(nèi)META-INF/MANIFEST.MF文件中的Main-Class屬性值為org.springframework.boot.loader.JarLauncher,可以看出main函數(shù)是JarLauncher,即:SpringBoot應(yīng)用中的Main-class屬性指向的class為org.springframework.boot.loader.JarLauncher。

其實(shí)吧,主要是 Java官方文檔規(guī)定:java -jar命令引導(dǎo)的具體啟動(dòng)類必須配置在MANIFEST.MF資源的Main-class屬性中;又根據(jù)“JAR文件規(guī)范”,MANIFEST.MF資源必須存放在/META-INF/目錄下。所以main函數(shù)才是JarLauncher

JarLauncher類繼承圖如下:

請(qǐng)?zhí)砑訄D片描述

JarLauncher的類注釋我們看出JarLauncher的作用:

  • 加載內(nèi)部/BOOT-INF/lib下的所有三方依賴jar;
  • 加載內(nèi)部/BOOT-INF/classes下的所有應(yīng)用class;

1)JarLauncher的運(yùn)行步驟?

  • 在解壓jar包后的根目錄下運(yùn)行 java org.springframework.boot.loader.JarLauncher。項(xiàng)目引導(dǎo)類(META-INF/MANIFEST.MF文件中的Start-Class屬性)被JarLauncher加載并執(zhí)行。
  • 如果直接運(yùn)行Start-Class(示例的StartApplication)類,會(huì)報(bào)錯(cuò)ClassNotFoundException。
  • Spring Boot依賴的JAR文件均存放在BOOT-INF/lib目錄下。JarLauncher會(huì)將這些JAR文件作為Start-Class的類庫依賴。

這也是為什么JarLauncher能夠引導(dǎo),而直接運(yùn)行Start-Class卻不行。

2)JarLauncher實(shí)現(xiàn)原理?

public class JarLauncher extends ExecutableArchiveLauncher {

	static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";

	static final String BOOT_INF_LIB = "BOOT-INF/lib/";

	public JarLauncher() {
	}

	protected JarLauncher(Archive archive) {
		super(archive);
	}

	@Override
	protected boolean isNestedArchive(Archive.Entry entry) {
		if (entry.isDirectory()) {
			return entry.getName().equals(BOOT_INF_CLASSES);
		}
		return entry.getName().startsWith(BOOT_INF_LIB);
	}

	public static void main(String[] args) throws Exception {
		new JarLauncher().launch(args);
	}

}

JarLauncher#main()中新建了JarLauncher并調(diào)用父類Launcher中的launch()方法啟動(dòng)程序;

  • BOOT_INF_CLASSES、BOOT_INF_LIB變量對(duì)應(yīng)BOOT-INF/classes和lib路徑;
  • isNestedArchive(Archinve.Entry entry)方法用于判斷FAT JAR資源的相對(duì)路徑是否為nestedArchive嵌套文檔。進(jìn)而決定這些FAT JAR是否會(huì)被launch。 當(dāng)方法返回false時(shí),說明FAT JAR被解壓至文件目錄。

1> Archive的概念

archive即歸檔文件,這個(gè)概念在linux下比較常見;通常就是一個(gè)tar/zip格式的壓縮包;而jar正是zip格式的。

SpringBoot抽象了Archive的概念,一個(gè)Archive可以是jar(JarFileArchive),也可以是文件目錄(ExplodedArchive);這樣也就統(tǒng)一了訪問資源的邏輯層;

public interface Archive extends Iterable<Archive.Entry>, AutoCloseable {
    ....
}

Archive繼承自Archive.Entry,Archive.Entry有兩種實(shí)現(xiàn):

JarFileArchive.JarFileEntry --> 基于java.util.jar.JarEntry實(shí)現(xiàn),表示FAT JAR嵌入資源。

ExplodedArchive.FileEntry --> 基于文件系統(tǒng)實(shí)現(xiàn);

兩者的主要差別是ExplodedArchive相比于JarFileArchive多了一個(gè)獲取文件的getFile()方法;

public File getFile() {
    return this.file;
}

也就是說一個(gè)在jar包環(huán)境下尋找資源,一個(gè)在文件夾目錄下尋找資源;

所以從實(shí)現(xiàn)層面證明了JarLauncher支持JAR和文件系統(tǒng)兩種啟動(dòng)方式。

當(dāng)執(zhí)行java -jar命令時(shí),將調(diào)用/META-INF /MANIFEST.MF文件的Main-Class屬性的main()方法,實(shí)際上調(diào)用的是JarLauncher#launch(args)方法;

3) Launcher#launch(args)方法

protected void launch(String[] args) throws Exception {
    if (!isExploded()) {
        // phase1:注冊(cè)jar URL處理器
        JarFile.registerUrlProtocolHandler();
    }
    // phase2:創(chuàng)建ClassLoader
    ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
    String jarMode = System.getProperty("jarmode");
    String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass();
    // phase3:調(diào)用實(shí)際的引導(dǎo)類launch
    launch(args, launchClass, classLoader);
}

launch()方法分三步:

  • 注冊(cè)jar URL處理器;
  • 為所有的Archive創(chuàng)建可以加載jar in jar目錄的ClassLoader;
  • 調(diào)用實(shí)際的引導(dǎo)類(Start-Class);

1> phase1 注冊(cè)jar URL處理器

private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs";

	private static final String HANDLERS_PACKAGE = "org.springframework.boot.loader";

public static void registerUrlProtocolHandler() {
    String handlers = System.getProperty(PROTOCOL_HANDLER, "");
    System.setProperty(PROTOCOL_HANDLER, ("".equals(handlers) ? HANDLERS_PACKAGE
                                          : handlers + "|" + HANDLERS_PACKAGE));
    // 重置緩存的UrlHandlers;
    resetCachedUrlHandlers();
}

private static void resetCachedUrlHandlers() {
    try {
        // 由URL類實(shí)現(xiàn):通過URL.setURLStreamHandlerFactory()獲得URLStreamHandler。
        URL.setURLStreamHandlerFactory(null);
    }
    catch (Error ex) {
        // Ignore
    }
}

JarFile#resetCachedUrlHandlers()方法利用java.net.URLStreamHandler擴(kuò)展機(jī)制,實(shí)現(xiàn)由URL#getURLStreamHandler(String)提供。

URL#getURLStreamHandler(String protocol)方法:

首先,URL的關(guān)聯(lián)協(xié)議(Protocol)對(duì)應(yīng)一種URLStreamHandler實(shí)現(xiàn)類。

JDK內(nèi)建了一些協(xié)議的實(shí)現(xiàn),這些實(shí)現(xiàn)均存放在sun.net.www.protocol包下,并且類名必須為Handler,其類全名模式為sun.net.www.protocol.${protocol}.Handler(包名前綴.協(xié)議名.Handler),其中${protocol}表示協(xié)議名。

如果需要擴(kuò)展,則必須繼承URLStreamHandler類,通過配置Java系統(tǒng)屬性java.protocol.handler.pkgs,追加URLStreamHandler實(shí)現(xiàn)類的package,多個(gè)package以“|”分割。

所以對(duì)于SpringBoot的JarFile,registerURLProtocolHandler()方法將package org.springframework.boot.loader追加到j(luò)ava系統(tǒng)屬性java.protocol.handler.pkgs中。

也就是說,org.springframework.boot.loader包下存在協(xié)議對(duì)應(yīng)的Handler類,即org.springframework.boot.loader.jar.Handler;并且按照類名模式,其實(shí)現(xiàn)協(xié)議為JAR。

另外:在URL#getURLStreamHandler()方法中,處理器先讀取Java系統(tǒng)屬性java.protocol.handler.pkgs無論其是否存在,繼續(xù)讀取sun.net.www.protocol包;所以JDK內(nèi)建URLStreamHandler實(shí)現(xiàn)是兜底的。

為什么SpringBoot要選擇覆蓋URLStreamHandler?

  • Spring BOOT FAT JAR除包含傳統(tǒng)Java Jar資源之外,還包含依賴的JAR文件;即存在jar in jar的情況;
  • 默認(rèn)情況下,JDK提供的ClassLoader只能識(shí)別jar中的class文件以及加載classpath下的其他jar包中的class文件,對(duì)于jar in jar的包無法加載;
  • 當(dāng)SpringBoot FAT JAR被java -jar命令引導(dǎo)時(shí),其內(nèi)部的JAR文件無法被內(nèi)嵌實(shí)現(xiàn)sun.net.www.protocol.jar.Handler當(dāng)做class Path,故需要定義了一套URLStreamHandler實(shí)現(xiàn)類和JarURLConnection實(shí)現(xiàn)類,用來加載jar in jar包的class類文件;

2> phase2 創(chuàng)建可以加載jar in jar目錄的ClassLoader

獲取所有的Archive,然后針對(duì)每個(gè)Archive分別創(chuàng)建ClassLoader;

ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());

/**
 * 獲取所有的Archive(包含jar in jar的情況)
 */
protected Iterator<Archive> getClassPathArchivesIterator() throws Exception {
    return getClassPathArchives().iterator();
}

/**
 * 針對(duì)每個(gè)Archive分別創(chuàng)建ClassLoader
 */
protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
    List<URL> urls = new ArrayList<>(archives.size());
    for (Archive archive : archives) {
        urls.add(archive.getUrl());
    }
    return createClassLoader(urls.toArray(new URL[0]));
}

3> phase3 調(diào)用實(shí)際的引導(dǎo)類(Start-Class)

// case1: 通過ExecutableArchiveLauncher#getMainClass()獲取MainClass
String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass();
// 2、運(yùn)行實(shí)際的引導(dǎo)類
launch(args, launchClass, classLoader);

對(duì)于phase3,大致可以分為兩步:

  • 首先通過ExecutableArchiveLauncher#getMainClass()獲取mainClass(即:/META-INF/MANIFEST.MF資源中的Start-Class屬性);
  • 利用反射獲取mainClass類中的main(Stirng[])方法并調(diào)用;

<1> 獲取mainClass:

在這里插入圖片描述

Start-Class屬性來自/META_INF/MANIFEST.MF資源中。Launcher的子類JarLauncherWarLauncher沒有實(shí)現(xiàn)getMainClass()方法。所以無論是Jar還是War,讀取的SpringBoot啟動(dòng)類均來自此屬性。

<2> 執(zhí)行mainClass的main()方法:

獲取mainClass之后,MainMethodRunner#run()方法利用反射獲取mainClass類中的main(Stirng[])方法并調(diào)用。

在這里插入圖片描述

運(yùn)行JarLauncher實(shí)際上是在同進(jìn)程、同線程內(nèi)調(diào)用Start-Class類的main(Stirng[])方法,并且在調(diào)用前準(zhǔn)備好Class Path。

4、WarLauncher

WarLauncher是可執(zhí)行WAR的啟動(dòng)器。

WarLauncher與JarLauncher的差異很小,主要區(qū)別在于項(xiàng)目文件和JAR Class Path路徑的不同。

  • 相比于FAT Jar的目錄,WAR增加了WEB-INF/lib-provided,并且該目錄僅存放<scope>provided</scope>的JAR文件。
  • 傳統(tǒng)的Servlet應(yīng)用的Class Path路徑僅關(guān)注WEB-INF/classes/和WEB-INF/lib/目錄,因此WEB-INF/lib-provided/中的JAR將被Servlet忽略

好處:打包后的WAR文件能夠在Servlet容器中兼容運(yùn)行。

所以JarLauncher和WarLauncher并無本質(zhì)區(qū)別。

5、總結(jié)

Spring Boot應(yīng)用Jar/War的啟動(dòng)流程:

Spring Boot應(yīng)用打包之后,生成一個(gè)Fat jar,包含了應(yīng)用依賴的所有三方j(luò)ar包和SpringBoot Loader相關(guān)的類。

Fat jar的啟動(dòng)Main函數(shù)是JarLauncher,它負(fù)責(zé)創(chuàng)建一個(gè)LaunchedURLClassLoader來加載BOOT-INF/classes目錄以及/BOOT-INF/lib下面的jar,并利用反射獲取mainClass類中的main(Stirng[])方法并調(diào)用。即:運(yùn)行JarLauncher實(shí)際上是在同進(jìn)程、同線程內(nèi)調(diào)用Start-Class類的main(Stirng[])方法,并且在調(diào)用前準(zhǔn)備好Class Path。

其他點(diǎn):

SpringBoot通過擴(kuò)展JarFile、JarURLConnection及URLStreamHandler,實(shí)現(xiàn)了jar in jar中資源的加載。

SpringBoot通過擴(kuò)展URLClassLoader --> LauncherURLClassLoader,實(shí)現(xiàn)了jar in jar中class文件的加載。

WarLauncher相比JarLauncher只是多加載WEB-INF/lib-provided目錄下的jar文件。

到此這篇關(guān)于SpringBoot應(yīng)用jar包啟動(dòng)原理詳解的文章就介紹到這了,更多相關(guān)SpringBoot jar包啟動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中equals和等號(hào)(==)的區(qū)別淺談

    java中equals和等號(hào)(==)的區(qū)別淺談

    java中equals和等號(hào)(==)的區(qū)別淺談,需要的朋友可以參考一下
    2013-05-05
  • Java中import導(dǎo)入的用法說明

    Java中import導(dǎo)入的用法說明

    這篇文章主要介紹了Java中import導(dǎo)入的用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 詳解Java?@Documented注解的作用

    詳解Java?@Documented注解的作用

    @Documented和@Deprecated注解長得有點(diǎn)像,@Deprecated是用來標(biāo)注某個(gè)類或者方法不建議再繼續(xù)使用,@Documented只能用在注解上,本文將通過示例詳細(xì)說說@Documented注解的作用,需要的可以參考一下
    2022-09-09
  • java生成csv文件亂碼的解決方法示例 java導(dǎo)出csv亂碼

    java生成csv文件亂碼的解決方法示例 java導(dǎo)出csv亂碼

    這篇文章主要介紹了java生成csv文件亂碼的解決方法,大家可以直接看下面的示例
    2014-01-01
  • Java中GC的工作原理詳細(xì)介紹

    Java中GC的工作原理詳細(xì)介紹

    這篇文章主要介紹了Java中GC的工作原理詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Springboot使用異步請(qǐng)求提高系統(tǒng)的吞吐量詳解

    Springboot使用異步請(qǐng)求提高系統(tǒng)的吞吐量詳解

    這篇文章主要介紹了Springboot使用異步請(qǐng)求提高系統(tǒng)的吞吐量詳解,和同步請(qǐng)求相對(duì),異步不需要等待響應(yīng),隨時(shí)可以發(fā)送下一次請(qǐng)求,如果是同步請(qǐng)求,需要將信息填寫完整,再發(fā)送請(qǐng)求,服務(wù)器響應(yīng)填寫是否正確,再做修改,需要的朋友可以參考下
    2023-08-08
  • 如何獲取java新IO的Path文件大小

    如何獲取java新IO的Path文件大小

    這篇文章主要介紹了如何獲取java新IO的Path文件大小,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • 基于java使用釘釘機(jī)器人向釘釘群推送消息

    基于java使用釘釘機(jī)器人向釘釘群推送消息

    這篇文章主要介紹了基于java使用釘釘機(jī)器人向釘釘群推送消息,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Maven發(fā)布項(xiàng)目 (jar包) 到Nexus私服中的操作

    Maven發(fā)布項(xiàng)目 (jar包) 到Nexus私服中的操作

    這篇文章主要介紹了Maven發(fā)布項(xiàng)目 (jar包) 到Nexus私服中的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Java從JDK源碼角度對(duì)Object進(jìn)行實(shí)例分析

    Java從JDK源碼角度對(duì)Object進(jìn)行實(shí)例分析

    這篇文章主要介紹了Java從JDK源碼角度對(duì)Object進(jìn)行實(shí)例分析,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12

最新評(píng)論

子洲县| 光山县| 长治市| 科技| 綦江县| 临颍县| 阿坝| 广饶县| 元氏县| 丰顺县| 赞皇县| 莱阳市| 贵定县| 五家渠市| 徐闻县| 东丽区| 鸡西市| 无棣县| 安陆市| 那坡县| 锦屏县| 桓仁| 密山市| 大石桥市| 即墨市| 海南省| 宜宾市| 盐边县| 平南县| 清新县| 临沭县| 响水县| 七台河市| 垣曲县| 东光县| 介休市| 策勒县| 边坝县| 渝北区| 万年县| 许昌县|