举例:Springboot的可执行demo.jar包
执行命令 java -jar demo.jar时,Java 虚拟机的第一步先扫描 demo.jar文件的内部结构,定位并读取 META-INF/MANIFEST.MF 文件,MAINIFEST.MF文件是demo.jar的说明书。
Springboot的可执行demo.jar包目录结构如下所示

/BOOT-INF/classes : 存放应用的编译类文件
/BOOT-INF/lib:存放所有依赖第三方的JAR文件
/META-INF/MANIFEST.MF: Java 归档文件的一个标准元数据文件,包含该 JAR 包的清单信息
/org.springframework.boot.loader:Springboot启动需要的相关类
执行核心步骤

获取到的MANIFEST.MF文件信息如下
makefile
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Archiver-Version: Plexus Archiver
Built-By: test
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.demo.DemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.7.18
Created-By: Apache Maven 3.8.2
Build-Jdk: 1.8.0_381
Main-Class: org.springframework.boot.loader.JarLauncher
Main-Class: org.springframework.boot.loader.JarLauncher ,指定了jar包的入口点
Start-Class: com.demo.DemoApplication,指定了应用程序的主类
org.springframework.boot.loader.JarLauncher的继承关系如下所示

java
public class JarLauncher extends ExecutableArchiveLauncher {
static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
if (entry.isDirectory()) {
return entry.getName().equals("BOOT-INF/classes/");
}
return entry.getName().startsWith("BOOT-INF/lib/");
};
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
@Override
protected boolean isPostProcessingClassPathArchives() {
return false;
}
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
}
@Override
protected String getArchiveEntryPathPrefix() {
return "BOOT-INF/";
}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
执行的是Jarlauncher.main方法,该方法先创建一个JarLauncher对象,继承于抽象类ExecutableArchiveLauncher,抽象类ExecutableArchiveLauncher的构造方法会创建一个Archive对象
java
public ExecutableArchiveLauncher() {
try {
this.archive = createArchive();
this.classPathIndex = getClassPathIndex(this.archive);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
protected final Archive createArchive() throws Exception {
ProtectionDomain protectionDomain = getClass().getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null;
String path = (location != null) ? location.getSchemeSpecificPart() : null;
if (path == null) {
throw new IllegalStateException("Unable to determine code source archive");
}
File root = new File(path);
if (!root.exists()) {
throw new IllegalStateException("Unable to determine code source archive from " + root);
}
return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
}
创建JarLauncher对象后会拿到整个Jar包的结构信息,包括BOOT-INF/classes、BOOT-INF/lib、META-INF等。之后执行JarLauncher对象的launch方法,实际调用的是父级抽象类的Launcher类中的launch方法
java
public abstract class Launcher {
protected void launch(String[] args) throws Exception {
if (!isExploded()) {
JarFile.registerUrlProtocolHandler();
}
// 这里创建自己的类加载器进行加载,具体类为LaunchedURLClassLoader
ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
String jarMode = System.getProperty("jarmode");
//getMainClass()方法会从MANIFEST.MF文件中获取Start-Class,
//执行其 main方法,也就是我们服务的 SpringApplication的main方法
String launchClass = (jarMode != null && !jarMode.isEmpty()) ?
JAR_MODE_LAUNCHER : getMainClass();
launch(args, launchClass, classLoader);
}
//启动
protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
//// 将创建的类加载器放到线程上下文中
Thread.currentThread().setContextClassLoader(classLoader);
//创建main runner执行
createMainMethodRunner(launchClass, args, classLoader).run();
}
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
return new MainMethodRunner(mainClass, args);
}
}
public abstract class ExecutableArchiveLauncher extends Launcher {
private static final String START_CLASS_ATTRIBUTE = "Start-Class";
@Override
protected String getMainClass() throws Exception {
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
mainClass = manifest.getMainAttributes().getValue(START_CLASS_ATTRIBUTE);
}
if (mainClass == null) {
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
}
return mainClass;
}
}
public class MainMethodRunner {
private final String mainClassName;
private final String[] args;
/**
* Create a new {@link MainMethodRunner} instance.
* @param mainClass the main class
* @param args incoming arguments
*/
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = (args != null) ? args.clone() : null;
}
public void run() throws Exception {
// 从线程上下文中的类加载器中加载我们的服务启动类
Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
// 反射获取到 main 方法进行执行
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.setAccessible(true);
mainMethod.invoke(null, new Object[] { this.args });
}
}
至此,我们应用程序主入口类的main方法被调用执行,所有应用程序类文件均可通过/BOOT-INF/classes加载,所有依赖的第三方jar均可通过/BOOT-INF/lib加载。