从源码角度3分钟理解SpringBoot的jar可以直接运行的原因和原理

在springboot项目中我们使用maven打包插件将项目打成一个jar之后,然后使用java -jar的命令就可以直接运行jar了,这是什么原理呢?今天来揭秘一下 springbo ot的jar直接运行的原理。

1、制作jar

(1)添加依赖

<dependencies> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-starter-web</artifactId> 
        </dependency> 
        <dependency> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-loader</artifactId> 
        </dependency> 
    </dependencies> 
    <build> 
        <p> 
            <!--  spring-boot 打包  --> 
            <p> 
                <groupId>org.springframework.boot</groupId> 
                <artifactId>spring-boot-maven-plugin</artifactId> 
                <executions> 
                    <execution> 
                        <goals> 
                            <goal>repackage</goal> 
                        </goals> 
                    </execution> 
                </executions> 
            </plugin> 
        </plugins> 
    </build>

(2)编写基础的启动代码和测试类

------------------------启动类---------------------- 
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) 
public class SpringJarApplication { 
    public static void main(String[] args) { 
        SpringApplication.run(SpringJarApplication.class, args); 
    } 
} 
------------------------测试类------------------------ 
@RestController 
@RequestMapping("/test") 
public class SpringJarController { 
    @GetMapping("/test01") 
    public String test01() { 
        return "test01"; 
    } 
} 
------------------------配置文件------------------------ 
server: 
  port: 8080 
spring: 
  application: 
    name: spring-jar

(3)使用springboot的插件打包

打包成功之后的日志输出:

到这里就完成使用springboot插件打包了一个jar。使用java -jar的命令运行的效果如下所示:

2、解析jar的结构

在本地的仓库中找到刚打的jar,然后通过工具打开springboot插件打包jar包,如下所示:

(1)BOOT-INF包

BOOT -INF包含应用程序的所有类文件和资源,在BOOT-INF包下的lib中将项目中需要依赖的jar都打包进来了,因此我们又称springboot通过插件打包的jar叫做fat jar。

(2)META-INF包

这个包下包含了一份很重要的清单文件,此文件包含了应用程序的元数据信息,如下是清单文件的详细信息:

Manifest-Version: 1.0 
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx 
Implementation-Title: spring.jar 
Implementation-Version: 1.0-SNAPSHOT 
Start-Class: com.longxia.jar.SpringJarApplication 
Spring-Boot-Classes: BOOT-INF/classes/ 
Spring-Boot-Lib: BOOT-INF/lib/ 
Build-Jdk-Spec: 1.8 
Spring-Boot-Version: 2.3.2.RELEASE 
Created-By: Maven Jar Plugin 3.2.0 
Main-Class: org.springframework.boot.loader.JarLauncher

我们需要重点关注的两处,一处是Main-Class,因为它指定了应用的主类,也就是jar包的入口位置;一处是Start-Class,它标识着项目的启动类位置。

3、jar的运行原理

在运行jar包的时候,首先会找到MANIFEST.MF下的JarLauncher类,此类的就是程序的入口,JarLauncher类的代码如下所示:

Fat jar在启动JarLauncher中的main函数的时候, Fat jar会负责创创建一个LunchedURLClassLoader来加载 BOOT -INF包下的lib中的jar,如下所示:

接下来在launch方法中寻找我们项目中的真正的启动类,那么如何寻着启动类呢?其实是文件清单中的Start-class中找,如下所示:

找到项目启动类之后,通过反射的方式启动我们的项目,于是乎我们可以看到经典的启动图标:

总结:

(1)springboot提供的打包插件可以将应用程序打包成一个可执行的fat jar,在fat jar中包含了项目所需要的依赖和springboot loader相关的类信息。

(2)当执行java -jar命令的时候会先去MAINFEST.MF这个清单文件中寻找jar的启动入口(也就是Main-Class中指定的JarLauncher),然后在JarLauncher类下的launch方法中找到真正的应用启动类来执行。

9