TFdream / blog

个人技术博客,博文写在 Issues 里。
Apache License 2.0
129 stars 18 forks source link

SpringBoot jar 可直接运行原理 #301

Open TFdream opened 4 years ago

TFdream commented 4 years ago

在之前的一篇文章 Spring Boot干货系列:启动原理解析 分析了Spring Boot自动装配的原理,今天来看 SpringBoot 默认打包成的 jar (又称 fat jar)可直接运行的原理。

注:本篇Spring Boot版本为 2.1.8.RELEASE

Spring Boot fat jar结构

SpringBoot提供了一个插件spring-boot-maven-plugin用于把程序打包成一个可执行的jar包。在pom文件里加入这个插件即可:

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

打包完生成的executable-jar-1.0-SNAPSHOT.jar内部的结构如下:

然后可以直接执行jar包就能启动程序了:

java -jar executable-jar-1.0-SNAPSHOT.jar

打包出来fat jar内部有4种文件类型:

MANIFEST.MF文件的内容:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: apple
Start-Class: io.dreamstudio.operation.admin.AdminBootstrap
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.2.2.RELEASE
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_171
Main-Class: org.springframework.boot.loader.JarLauncher

我们看到,它的Main-Class是org.springframework.boot.loader.JarLauncher,当我们使用java -jar执行jar包的时候会调用JarLauncher的main方法,而不是我们编写的SpringApplication。

那么JarLauncher这个类是的作用是什么的?

它是SpringBoot内部提供的工具Spring Boot Loader提供的一个用于执行Application类的工具类(fat jar内部有spring loader相关的代码就是因为这里用到了)。相当于Spring Boot Loader提供了一套标准用于执行SpringBoot打包出来的jar。

JarLauncher的执行过程

JarLauncher的main方法:

package org.springframework.boot.loader;

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被构造的时候会调用父类ExecutableArchiveLauncher的构造方法。

ExecutableArchiveLauncher的构造方法内部会去构造Archive,这里构造了JarFileArchive。构造JarFileArchive的过程中还会构造很多东西,比如JarFile,Entry …

相关资料