Spring Boot可執行Jar包運行原理



Spring Boot有一個很方便的功能就是可以將應用打成可執行的Jar。那么大家有沒想過這個Jar是怎么運行起來的呢?本篇博客就來介紹下Spring Boot可執行Jar包的運行原理。


1. 打可執行Jar包

將Spring Boot應用打成可執行Jar包很容易,只需要在pom中加上一個Spring Boot提供的插件,然后在執行mvn package即可。

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

注意:

如果你的項目沒有繼承spring-boot-starter-parent這個POM,你需要做如下配置,將目標綁定到repackage。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

關於這個插件的詳細使用,可以參考官網

運行完mvn package后,我們會在target目錄下看到兩個jar文件。myproject-0.0.1-SNAPSHOT.jar和myproject-0.0.1-SNAPSHOT.jar.original。第一jar文件就是我們應用的可執行jar包,第二個Jar文件是應用原始的jar包。

2. 可執行Jar包內部結構

將打出來的可執行Jar解壓開我們能看到下面的結構:

可執行jar 目錄結構
├─BOOT-INF
│  ├─classes
│  └─lib
├─META-INF
│  ├─maven
│  ├─app.properties
│  ├─MANIFEST.MF      
└─org
    └─springframework
        └─boot
            └─loader
                ├─archive
                ├─data
                ├─jar
                └─util

我們先來重點關注兩個地方:META-INF下面的Jar包描述文件和BOOT-INF這個目錄。

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: xxxx
Start-Class: com.xxxx.AppServer
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.1.6.RELEASE
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_73
Main-Class: org.springframework.boot.loader.JarLauncher

在上面我們看到一個熟悉的配置Main-Class: org.springframework.boot.loader.JarLauncher。我們大概能猜到這個類是整個系統的入口。

再看下BOOT-INF這個目錄下面,我們會發現里面是我們項目打出來的class文件和項目依賴的Jar包。看到這里,你可能已經猜到Spring Boot是怎么啟動項目的了。

3. JarLauncher

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 {
        //項目入口,重點在launch這個方法中
		new JarLauncher().launch(args);
	}

}
//launch方法
protected void launch(String[] args) throws Exception {
    JarFile.registerUrlProtocolHandler();
    //創建LaunchedURLClassLoader。如果根類加載器和擴展類加載器沒有加載到某個類的話,就會通過LaunchedURLClassLoader這個加載器來加載類。這個加載器會從Boot-INF下面的class目錄和lib目錄下加載類。
    ClassLoader classLoader = createClassLoader(getClassPathArchives());
    //這個方法會讀取jar描述文件中的Start-Class屬性,然后通過反射調用到這個類的main方法。
    launch(args, getMainClass(), classLoader);
}

4. 簡單總結

  • Spring Boot可執行Jar包的入口點是JarLauncher的main方法;
  • 這個方法的執行邏輯是先創建一個LaunchedURLClassLoader,這個加載器加載類的邏輯是:先判斷根類加載器和擴展類加載器能否加載到某個類,如果都加載不到就從Boot-INF下面的class和lib目錄下去加載;
  • 讀取Start-Class屬性,通過反射機制調用啟動類的main方法,這樣就順利調用到我們開發的Spring Boot主啟動類的main方法了。

5. 遠程調試

這邊介紹個調試JarLauncher啟動的小技巧--IDEA 遠程調試

step1:我們以下面的方式啟動我們的Spring Boot應用

 java -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=y -jar App.jar

step2:在idea中進行如下配置

然后點擊下面的執行按鈕執行即可:

6. 公眾號推薦

歡迎大家關注我的個人公眾號「程序員自由之路」


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM