springboot jar包運行原理解析

 

大家都知道springboot應用打成jar包後可以直接運行,那jar包到底是怎麼運行起來的呢?

首先我們可以把jar包打開看一下里面的目錄結構:

META-INF:程序入口,其中MANIFEST.MF用於描述jar包的信息

BOOT-INF/lib:放置第三方依賴的jar包

BOOT-INF/classes:應用自己的代碼org:spring boot loader相關的代碼

org:spring boot loader相關的代碼

 

我們先看一下META-INF/MANIFEST.MF文件裏面都有些什麼

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

當我們使用java -jar 執行jar包的時候,會執行Main-Class指定類的main方法,org.springframework.boot.loader.JarLauncher這個類是springboot loader自己的一個類,我們來看一下里面的main方法具體實現

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

launch方法的具體實現在org.springframework.boot.loader.Launcher類裏,主要是創建自定義 ClassLoader 實現類 LaunchedURLClassLoader,通過它來加載 BOOT-INF/classes 目錄下的類,以及 BOOT-INF/lib 目錄下的 jar 包中的類,引入自定義類加載器就是爲了能解決jar包嵌套jar包的問題,系統自帶的AppClassLoarder不支持讀取嵌套jar包

protected void launch(String[] args) throws Exception {
        if (!this.isExploded()) {
            JarFile.registerUrlProtocolHandler();
        }
        //創建自定義 ClassLoader 實現類 LaunchedURLClassLoader
        ClassLoader classLoader = this.createClassLoader(this.getClassPathArchivesIterator());
        String jarMode = System.getProperty("jarmode");
        //獲取運行的mainclass
        String launchClass = jarMode != null && !jarMode.isEmpty() ? "org.springframework.boot.loader.jarmode.JarModeLauncher" : this.getMainClass();
        this.launch(args, launchClass, classLoader);
    }


protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
        Thread.currentThread().setContextClassLoader(classLoader);
        //執行自定義類的main方法
        this.createMainMethodRunner(launchClass, args, classLoader).run();
    }


protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
        return new MainMethodRunner(mainClass, args);
    }

接下來我們看一下getMainClass方法,是怎麼去加載我們自己的類,是通過讀取MANIFEST.MF文件裏的Start-Class屬性,找到我們自己的類

protected String getMainClass() throws Exception {
        Manifest manifest = this.archive.getManifest();
        String mainClass = null;
        if (manifest != null) {
            mainClass = manifest.getMainAttributes().getValue("Start-Class");
        }

        if (mainClass == null) {
            throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
        } else {
            return mainClass;
        }
    }

MainMethodRunner類裏主要是通過反射生成shi'l執行我們自定義類裏的main方法

public class MainMethodRunner {
    private final String mainClassName;
    private final String[] args;

    public MainMethodRunner(String mainClass, String[] args) {
        this.mainClassName = mainClass;
        this.args = args != null ? (String[])args.clone() : null;
    }

    public void run() throws Exception {
        Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
        Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
        mainMethod.setAccessible(true);
        mainMethod.invoke((Object)null, this.args);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章