一文搞懂Spring Boot中java -jar的启动jar包的原理
Spring Boot中通过java -jar启动jar包原理
SpringBoot源码系列:
一文搞懂Spring Boot中java -jar启动jar包的原理
一文搞懂SpringBoot启动流程及自动配置
一文搞懂SpringBoot内嵌的Tomcat
一文搞懂SpringApplication对象的构建及spring.factories的加载时机
一文搞懂Spring Boot 事件监听机制
今天我们研究一下Spring Boot中通过java -jar启动jar包原理
1、Spring Boot中的jar包与普通jar包有什么不同?
Spirng Boot中打出来的jar只能运行,不能被其他项目依赖。Spirng Boot打出来的jar中\BOOT-INF\classes目录下的才是我们的代码。
普通的jar可以被其他项目引用,解压后就是包名,包里就是我们的代码
2、Spring Boot打包出来的jar包
springbootdemo-0.0.1-SNAPSHOT.jar
springbootdemo-0.0.1-SNAPSHOT.jar.original
springbootdemo-0.0.1-SNAPSHOT.jar是spring-boot-maven-plugin生成的jar包。包含了应用的第三方依赖,spring boot相关的类,存在嵌套的jar包,称之为executable jar或fat jar。springbootdemo-0.0.1-SNAPSHOT.jar.original是默认的maven-jar-plugin生成的包,仅包含编译用的本地资源。
3、MANIFEST.MF文件文件内容(下面源码分析中getMainClass()实际就是获取了该文件中的Start-Class属性的属性值)
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: demo
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.example.springbootdemo.demo.DemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.6.4
Created-By: Maven JAR Plugin 3.2.2
Main-Class: org.springframework.boot.loader.JarLauncher
4、Debug jar包的配置。
直接Debug该jar包即可
4、当我们执行java -jar命令时他会找到/META-INF/MANIFEST.MF文件中的Main-Class属性,从而找到应用程序执行入口类org.springframework.boot.loader.JarLauncher,该类中定义了main()从而我们找到了程序入口。在该方法中构建了JarLauncher并调用了launch()方法。
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
实际调用的是Launcher中的launch(String[] args)方法。因为JarLauncher继承了ExecutableArchiveLauncher,而ExecutableArchiveLauncher又继承了Launcher。
接下来我们看一下这个launch(String[] args)方法。
protected void launch(String[] args) throws Exception {
if (!isExploded()) {
// Launcher(启动器)通过exploded mode启动
//registerUrlProtocolHandler()注册一个java.protocol.handler.pkgs属性以便找到URLStreamHandler来处理jar URLs
//该方法捕获了Jar上下文URL,且通过java.protocol.handler.pkgs属性设置了org.springframework.boot.loader
JarFile.registerUrlProtocolHandler();
}
//getClassPathArchivesIterator()返回用于构造类路径的Archive(即是一个归档文件,通常是一个tar/zip文件,jar是zip格式)
//createClassLoader()根据Archive中的URLS创建LaunchedURLClassLoader
ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
//获取jarmode属性
String jarMode = System.getProperty("jarmode");
//此时jarmode属性为空,通过getMainClass()从Manifest中获取Start-Class(启动类)的值
String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass();
launch(args, launchClass, classLoader);
}
继续关注上述launch(String[] args)方法中的最后一步launch(args, launchClass, classLoader)方法。
protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
//设置当前线程的类加载器为LaunchedURLClassLoader
Thread.currentThread().setContextClassLoader(classLoader);
//创建MainMethodRunner对象,调用它的run()方法来运行我们DemoApplication中的main()方法。
createMainMethodRunner(launchClass, args, classLoader).run();
}
我们看一下MainMethodRunner中的run()方法是如何调用我们DemoApplication中的main()方法。
public void run() throws Exception {
//首先通过反射获取到了main()方法所在主类
Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
//根据这个主类获取其中的main()方法
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
//取消安全检查提升反射速度
mainMethod.setAccessible(true);
//实现main()动态调用
mainMethod.invoke(null, new Object[] { this.args });
}
至此Spring Boot中 java -jar启动jar包的整个流程结束,接下来进入整个Spring Boot项目的启动流程。
下面我们总结一下Spring Boot中 java -jar启动jar包的整个流程
更多推荐
所有评论(0)