一、概述
使用过SprongBoot打过jar包的都应该知道,目标文件一般都会生成两个文件,一个是以.jar的包,一个是.jar.original文件。那么使用SpringBoot会打出两个包,而.jar.original的作用是什么呢?还有就是java -jar是如何将一个SpringBoot项目启动的呢?
其实.jar.original是在SpringBoot重新打包之前的原始jar包,内部只包含了项目的用户类,不包含其他的依赖jar包,生成之后,SpringBoot会重新打包之后,最后生成.jar包,内部包含了原始jar包以及其他的引用依赖。
SpringBoot Maven插件打包配置
org.springframework.boot
spring-boot-maven-plugin
2.7.5
//指定SpringBoot项目的main函数的主类入口
xxx.xxx. ..server.bootstrap.LayoutServerApplication
//打成jar包
JAR
repackage
xxx.jar.original文件
只包含用户项目中编写的类及其资源信息,不包含其他依赖jar包
xxx.jar文件
内部包含了原始jar包以及其他的引用依赖jar包
二、SpringBoot xxx.jar包内部结构解析
- BOOT-INF:这个文件夹下有两个文件夹classes用来存放用户类,也就是原始jar.original里的类;还有一个是lib,就是这个原始jar.original引用的依赖。
- META-INF:这里是通过java -jar启动的入口信息,记录了入口类的位置等信息。
- org:org.springframework.boot.loader包下的字节码文件,通过它来启动应用。
这里主要介绍META-INF/MANIFEST.MF文件
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Archiver-Version: Plexus Archiver
Built-By: admin
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: xxx.layout.server.bootstrap.LayoutServerApplication //项目main函数的主类入口
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.7.5
Created-By: Apache Maven 3.5.2
Build-Jdk: 1.8.0_261
Main-Class: org.springframework.boot.loader.JarLauncher //jar包启动类
三、启动过程解析
从MANIFEST.MF文件中的Main-class指向入口开始。
创建JarLauncher并且通过它的launch()方法开始加载jar包内部信息。
//org.springframework.boot.loader.JarLauncher的main()方法
public static void main(String[] args) throws Exception {
(new JarLauncher()).launch(args);
}
JarLauncher的父类ExecutableArchiveLauncher会去加载jar中的所有文件
public ExecutableArchiveLauncher() {
try {
this.archive = this.createArchive(); //加载归档文件
this.classPathIndex = this.getClassPathIndex(this.archive);
} catch (Exception var2) {
throw new IllegalStateException(var2);
}
}
获取项目中指定的main函数主类
protected void launch(String[] args) throws Exception {
if (!this.isExploded()) {
JarFile.registerUrlProtocolHandler();
}
ClassLoader classLoader = this.createClassLoader(this.getClassPathArchivesIterator());
String jarMode = System.getProperty("jarmode");
//this.getMainClass()获取主类方法
String launchClass = jarMode != null && !jarMode.isEmpty() ? "org.springframework.boot.loader.jarmode.JarModeLauncher" : this.getMainClass();
this.launch(args, launchClass, classLoader);
}
protected String getMainClass() throws Exception {
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
//META-INF/MANIFEST.MF文件中的Start-Class属性值,即项目中自定义的启动主类
mainClass = manifest.getMainAttributes().getValue("Start-Class");
}
if (mainClass == null) {
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
} else {
return mainClass;
}
}
通过反射调用项目中指定启动主类的main方法
protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
this.createMainMethodRunner(launchClass, args, classLoader).run();
}
public void run() throws Exception {
Class> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.setAccessible(true);
//反射调用main方法
mainMethod.invoke((Object)null, this.args);
}
//项目中的主类main方法被调用,SpringBoot应用启动
@SpringBootApplication
public class LayoutServerApplication
{
private static final Logger logger = LoggerFactory.getLogger(LayoutServerApplication.class);
public static void main(String[] args)
{
SpringApplication.run(LayoutServerApplication.class, args);
logger.info("-------layout-server启动完成-----");
}
}