分析Spring Boot Loader源码其实就是在搞懂为何Spring Boot打成jar包可以直接运行起来。
一)、探索从解压jar包开始
解压项目打包成的jar包,我们可以得到三个文件夹
BOOT-INF
,内部还有两个文件夹classes(存放项目相关字节码文件)和lib(存放项目所依赖第三方jar包)
-
META-INF
,关于项目的一些元数据Manifest-Version: 1.0 Implementation-Title: spring_boot_dissect Implementation-Version: 1.0 Start-Class: top.yangzefeng.dissect.SpringBootDissectApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.2.7.RELEASE Created-By: Maven Archiver 3.4.0 Main-Class: org.springframework.boot.loader.JarLauncher
-
org
,其实是 Spring Boot 提供的spring-boot-loader
项目,即便我们项目中并没有明确指定loader的存在,但Spring Boot也会帮我们将其代码拷贝放到jar包内。<!-- 如果需要研究loader的源码,我们还得在pom.xml中引入相关依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-loader</artifactId> <version>2.2.7.RELEASE</version> </dependency>
二)、loader源码分析从JarLauncher出发
我们解压jar包得到META-INF文件夹,查看META-INF文件夹的MANIFEST.MF文件内容。
最为关键的两个属性:
Start-Class
,为我们项目启动类的全类路径Main-Class
,为JarLauncher的全类路径,即Java规定的启动jar包类
Start-Class: top.yangzefeng.dissect.SpringBootDissectApplication
Main-Class: org.springframework.boot.loader.JarLauncher
由Main-Class的属性值,我们可以得出,要想分析Spring Boot Loader的源码,得从org.springframework.boot.loader.JarLauncher开始。
三)、JarLauncher
/**
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
* included inside a {@code /BOOT-INF/lib} directory and that application classes are
* included inside a {@code /BOOT-INF/classes} directory.
*/
public class JarLauncher extends ExecutableArchiveLauncher {
public JarLauncher() {
}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
从JarLauncher的main方法开始。
-
1、创建JarLauncher对象,我们可以看到,JarLauncher的无参构造方法没有代码,但由于JarLauncher类继承了ExecutableArchiveLauncher抽象类,所以会调用ExecutableArchiveLauncher的无参构造方法。
-
2、ExecutableArchiveLauncher的无参构造方法主要是为了创建JarFileArchive对象并进行字段赋值。
其中createArchive()由Launcher进行调用。
public abstract class ExecutableArchiveLauncher extends Launcher { private final Archive archive; public ExecutableArchiveLauncher() { try { this.archive = createArchive(); } catch (Exception ex) { throw new IllegalStateException(ex); } } }