面试官居然我问SpringBoot 是如何通过jar包启动的?- 第373篇

对于SpringBoot打包的jar文件,我们呢,只通过java -jar一行命令便可以启动一个web项目,这如此神奇的效果是SpringBoot是如何帮你完成的呢?

一、从spring-boot-maven-plugin说起

       对于SpringBoot项目,我们会在pom.xml文件添加打包插件spring-boot-maven-plugin,那么执行打包的时候,会生成相应的jar文件,比如:

spring-boot-hello-0.0.1-SNAPSHOT.jar

       我们打开这个jar看下都有什么呢?

       通过这里我们可以看出spring-boot-maven-plugin做了几个重要的事情:

(1)生成核心的文件MANIFEST.MF;

(2)把依赖的jar包进行打包;

       这里普及一个知识点:jar里打包进去了别的jar包,这样的jar称为fat jar(胖的jar)。

       标准的解释:在java中,通常将应用程序及其依赖jar一起打包到一个独立的jar中,就叫fat jar,它也叫uberJar。

       在上面最重要的信息就是:MANIFEST.MF,我们进去一探究竟。

二、进入MANIFEST.MF一探究竟

       我们脱去MANIFEST.MF的外衣看看里面是什么鬼?

       我们把jar解压,然后使用记事本打开MANIFEST.MF文件:

       在MANIFEST.MF文件中,重要的是两个属性是:

Start-Class: com.kfit.springboothellosts.SpringBootHelloStsApplication

Main-Class: org.springframework.boot.loader.JarLauncher

       我们会发现Start-Class指向的是我们的启动类,也就是注解了@SpringBootApplication的类。

       Main-Class又是什么呢?接着往下看,为您揭晓。

三、java -jar做了什么?

       先要弄清楚java -jar命令做了什么,在oracle官网找到了该命令的描述:

        If the -jar option is specified, its argument is the name of the JAR file containing class and resource files for the application. The startup class must be indicated by the Main-Class manifest header in its source code.

       翻译过来的大体意思就是:使用-jar参数时,后面的参数是jar的文件名称(本例子中是spring-boot-hello-0.0.1-SNAPSHOT.jar),该jar文件中包含的是class和资源文件;在mainfest文件中有Main-Class的定义;Main-Class的源码中指定了整个应用的启动类。

       简单来说就是:java -jar会去找jar中的manifest文件,找到属性属性Main-Class,从而找到真正的启动类。

四、JarLauncher是什么东东呢?

       对于Spring Boot项目的Main-Class中的值是org.springframework.boot.loader.JarLauncher,那么当我们执行命令:

Java -jar  spring-boot-hello-0.0.1-SNAPSHOT.jar

的时候,那么会找到.jar文件中的

Main-Class属性值org.springframework.boot.loader.JarLauncher,然后执行JarLauncher文件。

       那么JarLauncher是什么?

       JarLauncher实际上是一个Jar的启动器,那么它核心作用就是:

加载jar包的jar文件和class文件

       加载完成之后会找到Start-Class指定的启动类,通过反射进行启动应用。

五、为什么Spring Boot要自定义一个ClassLoader

       这个就和java有关系了,对于嵌套的jar,这里有很重要的一句话:

Java没有提供任何标准的方式来加载嵌套的jar文件(即,它们本身包含在jar中的jar文件)。

       所以对于Spring Boot项目中依赖的jar文件,java并无能为力,对于-jar的底层是找到一个Main-Class属性值,对于Spring Boot项目要启动,有两个核心的事情就要去做了:

(1)加载jar中的jar文件以及class文件;

(2)启动main方法;

       对于main方法启动类上的注解@SpringBootApplication注解是在相应的jar中的,很显然不能直接先启动main方法中的类,然后再加载jar文件。

       所以需要Main-Class就不能指向main方法中的启动类了,对于SpringBoot定义了一个JarLauncher来加载jar文件和class文件,对于jar文件和class文件的加载的工作在java中是由classloader来完成的,java内置的classloader不能满足要求,也就需要Spring Boot自定义ClassLoader来搞定这个事情了,也就是JarLauncher是一个自定义的类加载器。

       对于Starter-Class是Spring Boot自己定义的一个属性值,为了JarLauncher加载了jar文件和class文件之后,可以找到启动类。

六、在IDE如何启动Spring boot应用

       在上面只提到在一个fat jar里启动SpringBoot应用的过程,那么IDE里Spring boot是如何启动的呢? 在IDE里,直接运行的Main函数是应用的Main函数:

       在IDE里启动SpringBoot应用是最简单的一种情况,因为依赖的Jar都让IDE放到classpath里了,所以Spring boot 直接启动就完事了。

七、JarLauncher扩展加载说明

(1)JarLauncher通过加载BOOT-INF/classes目录及BOOT-INF/lib目录下jar文件,实现了fat jar的启动。

(2)SpringBoot通过扩展JarFile、JarURLConnection及URLStreamHandler,实现了jar in jar中资源的加载。

(3)SpringBoot通过扩展URLClassLoader–LauncherURLClassLoader,实现了jar in jar中class文件的加载。

       另外一个WarLauncher是:WarLauncher通过加载WEB-INF/classes目录及WEB-INF/lib和WEB-INF/lib-provided目录下的jar文件,实现了war文 件的直接启动及web容器中的启动。

总结

       总结一下Spring Boot应用的启动流程:

(1)Spring Boot应用打包之后,生成一个Fat jar,包含了应用依赖的jar包和Spring Boot loader相关的类。

(2)Fat jar的启动Main函数是JarLauncher,它负责创建一个LaunchedURLClassLoader来加载/lib下面的jar,并以一 个新线程启动应用的Main函数。


购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287