本周(8.21-8.27)将学习芋道 Spring Boot的以下文章:
8.21: 快速入门
**8.22:**Spring Boot 自动配置原理 、Jar 启动原理
8.23:调试环境、 热部署入门、消除冗余代码 Lombok 入门
8.24:对象转换 MapStruct 入门、SpringMVC 入门
8.25: WebFlux 入门、 分布式 Session 入门
8.26:API 接口文档 Swagger 入门、API 接口文档 Swagger Starter 入门
8.27:参数校验 Validation 入门、WebSocket 入门
芋道 Spring Boot Jar 启动原理
-
Spring Boot 提供了 Maven 插件
spring-boot-maven-plugin
,可以方便的将 Spring Boot 项目打成jar
包或者war
包。 -
Jar包的结构组成
-
①
META-INF
目录:通过MANIFEST.MF
文件提供jar
包的元数据,声明了jar
的启动类。 -
②
org
目录:为 Spring Boot 提供的spring-boot-loader
项目,它是java -jar
启动 Spring Boot 项目的秘密所在,也是稍后我们将深入了解的部分。Spring Boot Loader provides the secret sauce that allows you to build a single jar file that can be launched using
java -jar
. Generally you will not need to usespring-boot-loader
directly, but instead work with the Gradle or Maven plugin. -
③
BOOT-INF/lib
目录:我们 Spring Boot 项目中引入的依赖的jar
包们。spring-boot-loader
项目很大的一个作用,就是解决jar
包里嵌套jar
的情况,如何加载到其中的类。spring-boot-loader
项目需要解决两个问题:- 第一,如何引导执行我们创建的 Spring Boot 应用的启动类,例如上述图中的 Application 类。
- 第二,如何加载
BOOT-INF/class
目录下的类,以及BOOT-INF/lib
目录下内嵌的jar
包中的类。
-
④
BOOT-INF/classes
目录:我们在 Spring Boot 项目中 Java 类所编译的.class
、配置文件等等。
-
-
META-INF/MANIFEST.MF
文件,里面的内容如下:Manifest-Version: 1.0 Implementation-Title: lab-39-demo Implementation-Version: 2.2.2.RELEASE Start-Class: cn.iocoder.springboot.lab39.skywalkingdemo.Application Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.2.2.RELEASE Created-By: Maven Archiver 3.4.0 Main-Class: org.springframework.boot.loader.JarLauncher
-
实际是一个 Properties 配置文件,每一行都是一个配置项目。重点来看看两个配置项:
Main-Class
配置项:Java 规定的jar
包的启动类,这里设置为spring-boot-loader
项目的 JarLauncher 类,进行 Spring Boot 应用的启动。Start-Class
配置项:Spring Boot 规定的主启动类,这里设置为我们定义的 Application 类。
小知识补充:为什么会有
Main-Class
/Start-Class
配置项呢?因为我们是通过 Spring Boot 提供的 Maven 插件spring-boot-maven-plugin
进行打包,该插件将该配置项写入到MANIFEST.MF
中,从而能让spring-boot-loader
能够引导启动 Spring Boot 应用。- 直接运行
Start-Class
配置项的主启动类(main函数)是无法启动的(提示找不到或无法加载主类),主要是因为打包成的jar的不符合java默认加载jar包的规则(/WEB-INF/classes
下的class文件,而Start-Class的Aplication类被打包在BOOT-INF/classes
下,其次Java 规定可执行器的jar
包禁止嵌套其它jar
包,而maven项目需要加载BOOT-INF/lib
目录下Spring Boot 应用依赖的所有jar
包),所以需要通过spring-boot-loader
项目自定义实现了 ClassLoader 实现类 LaunchedURLClassLoader,支持加载BOOT-INF/classes
目录下的.class
文件,以及BOOT-INF/lib
目录下的jar
包。
-
JarLauncher 类是针对 Spring Boot
jar
包的启动类,整体类图如下所示:代码如下:
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); } }
通过
#main(String[] args)
方法,创建 JarLauncher 对象,并调用其#launch(String[] args)
方法进行启动。整体的启动逻辑,其实是由父类 Launcher 所提供。 -
简单来说,就是做一个可以读取
jar
包中类的加载器,保证BOOT-INF/lib
目录下的类和BOOT-classes
内嵌的jar
中的类能够被正常加载到,之后执行 Spring Boot 应用的启动。 -
LaunchedURLClassLoader 是
spring-boot-loader
项目自定义的类加载器,实现对jar
包中META-INF/classes
目录下的类和META-INF/lib
内嵌的jar
包中的类的加载。 -
总体来说,Spring Boot
jar
启动的原理是非常清晰的,整体如下图所示:
红色部分,解决 jar
包中的类加载问题:
- 通过 Archive,实现
jar
包的遍历,将META-INF/classes
目录和META-INF/lib
的每一个内嵌的jar
解析成一个 Archive 对象。 - 通过 Handler,处理
jar:
协议的 URL 的资源读取,也就是读取了每个 Archive 里的内容。 - 通过 LaunchedURLClassLoader,实现
META-INF/classes
目录下的类和META-INF/classes
目录下内嵌的jar
包中的类的加载。具体的 URL 来源,是通过 Archive 提供;具体 URL 的读取,是通过 Handler 提供。
橘色部分,解决 Spring Boot 应用的启动问题:
- 通过 MainMethodRunner ,实现 Spring Boot 应用的启动类的执行。
当然,上述的一切都是通过 Launcher 来完成引导和启动,通过 MANIFEST.MF
进行具体配置。