Spring Boot 打包成Jar包運(yùn)行的原理

相比與傳統(tǒng)的Java打包方式,使用SpringBoot打包插件打包成jar包后,可以直接使用java -jar 運(yùn)行SpringBoot項目,本篇就來分析一下運(yùn)行的原理。

SpringBoot打包插件

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

打包完后會生成兩個文件,一個***.jar***.jar.original

.jar文件是SpringBoot打包后生成的文件,.jar.original是用原生方式打包生成的文件,對比一下兩個的區(qū)別

.jar.original文件

.jar文件

.jar.original就是普通的jar打包的結(jié)構(gòu),這里主要看.jar文件的結(jié)構(gòu):

  • META-INFO目錄:META-INFO/MANIFEST.MF里包含了jar包的元數(shù)據(jù),包含了項目的啟動類等信息.

  • org目錄:該目錄下包含的是啟動項目的一些類,啟動的過程就在這個包里。

  • BOOT-INFO目錄:本地項目的代碼(BOOT-INF/classes),以及所需的以依賴(BOOT-INFO/lib)

重點在META-INFO/MANIFEST.MF里:

Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: demo
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.example.demo.DemoApplication
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-Class :jar包啟動類,這是java規(guī)定的字段,存在這個字段的情況下, 在java -jar時,jar包才會運(yùn)行起來
  • Start-Class:本地項目的啟動類
  • Spring-Boot-Classes:加載應(yīng)用類的入口
  • Spring-Boot-Lib:項目所需的依賴

有了Main-Class啟動類,那就直接進(jìn)入到JarLauncher里查看運(yùn)行的過程

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

JarLaunchermain方法里調(diào)用了launch方法,launch方法的具體實現(xiàn)在JarLauncher的抽象父類Launcher中實現(xiàn)

    protected void launch(String[] args) throws Exception {
        if (!this.isExploded()) {
            JarFile.registerUrlProtocolHandler();
        }

        ClassLoader classLoader = this.createClassLoader(this.getClassPathArchivesIterator());
        String jarMode = System.getProperty("jarmode");
        String launchClass = jarMode != null && !jarMode.isEmpty() ? "org.springframework.boot.loader.jarmode.JarModeLauncher" : this.getMainClass();
        this.launch(args, launchClass, classLoader);
    }

  • 首先獲取了類加載器。

  • 然后獲取jarMode,再根據(jù)jarMode獲取launchClass,如果沒有設(shè)置jarMode,則根據(jù)getMainClass方法獲取,getMainClass的具體實現(xiàn)在ExecutableArchiveLauncher中實現(xiàn)

     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;
            }
        }
    

    getMainClass里獲取了MANIFEST.MF文件里Start-Class字段的值,也就是本地項目的啟動類。

  • 最后調(diào)用this.launch(args, launchClass, classLoader);

        protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
            Thread.currentThread().setContextClassLoader(classLoader);
            this.createMainMethodRunner(launchClass, args, classLoader).run();
        }
    

    調(diào)用MainMethodRunnerrun()方法

    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);
    }

run()方法里通過反射拿到了項目的啟動類的main方法,從而啟動本地項目。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容