直接找不到 Application 类,因为它在 BOOT-INF/classes目录下,不符合 Java 默认的 jar 包的加载规则。因此,需要通过 JarLauncher 启动加载。
当然实际还有一个更重要的原因,Java 规定可执行器的 jar 包禁止嵌套其它 jar 包。但是我们可以看到 BOOT-INF/lib 目录下,实际有 Spring Boot 应用依赖的所有 jar 包。因此,spring-boot-loader 项目自定义实现了 ClassLoader 实现类 LaunchedURLClassLoader,支持加载 BOOT-INF/classes 目录下的 .class 文件,以及 BOOT-INF/lib 目录下的 jar 包。
3. JarLauncher
JarLauncher 类是针对 Spring Boot jar 包的启动类,整体类图如下所示:
友情提示:WarLauncher 类,是针对 Spring Boot war 包的启动类,后续胖友可以自己瞅瞅,差别并不大哈~
JarLauncher 的源码比较简单,如下图所示:
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);
}
}
1. 概述
Spring Boot 提供了 Maven 插件 spring-boot-maven-plugin,可以方便的将 Spring Boot 项目打成 jar 包或者 war 包。
考虑到部署的便利性,我们绝大多数 99.99% 的场景下,我们会选择打成 jar 包。这样,我们就无需在部署项目的服务器上,配置相应的 Tomcat、Jetty 等 Servlet 容器。
那么,jar 包是如何运行,并启动 Spring Boot 项目的呢?这个就是本文的目的,一起弄懂 Spring Boot jar 包的运行原理。
下面,我们来打开一个 Spring Boot jar 包,看看其里面的结构。如下图所示,一共分成四部分:
先简单剧透下,spring-boot-loader 项目需要解决两个问题:
下面,尾随艿艿,一起来抽丝剥茧!
2. MANIFEST.MF
我们来查看 META-INF/MANIFEST.MF 文件,里面的内容如下:
它实际是一个 Properties 配置文件,每一行都是一个配置项目。重点来看看两个配置项:
可能胖友会有疑惑,Start-Class 对应的 Application 类自带了 #main(String[] args) 方法,为什么我们不能直接运行会如何呢?我们来简单尝试一下哈,控制台执行如下:
直接找不到 Application 类,因为它在
BOOT-INF/classes
目录下,不符合 Java 默认的 jar 包的加载规则。因此,需要通过 JarLauncher 启动加载。当然实际还有一个更重要的原因,Java 规定可执行器的 jar 包禁止嵌套其它 jar 包。但是我们可以看到 BOOT-INF/lib 目录下,实际有 Spring Boot 应用依赖的所有 jar 包。因此,
spring-boot-loader
项目自定义实现了 ClassLoader 实现类LaunchedURLClassLoader
,支持加载BOOT-INF/classes
目录下的 .class 文件,以及 BOOT-INF/lib 目录下的 jar 包。3. JarLauncher
JarLauncher 类是针对 Spring Boot jar 包的启动类,整体类图如下所示:
JarLauncher 的源码比较简单,如下图所示:
通过 #main(String[] args) 方法,创建 JarLauncher 对象,并调用其 #launch(String[] args) 方法进行启动。整体的启动逻辑,其实是由父类 Launcher 所提供,如下图所示:
父类 Launcher 的 #launch(String[] args) 方法,代码如下:
简单来说,就是整一个可以读取 jar 包中类的加载器,保证 BOOT-INF/lib 目录下的类和 BOOT-classes 内嵌的 jar 中的类能够被正常加载到,之后执行 Spring Boot 应用的启动。
下面,我们逐行代码来看看噢。即将代码多多,保持淡定,嘿嘿~
3.1 registerUrlProtocolHandler