Spring Boot 的 JAR 包可以直接运行的核心原理在于它采用了 可执行 JAR(Fat JAR) 技术,将应用代码、依赖库、嵌入式服务器(如 Tomcat、Jetty)以及资源文件整合到单个 JAR 文件中,并通过自定义的启动类实现自包含运行。以下是详细的原理剖析:
1. 可执行 JAR 的结构
Spring Boot 打包后的 JAR 文件包含以下关键部分:
plaintext
my-spring-boot-app.jar
├── META-INF/
│ └── MANIFEST.MF # 清单文件,指定主类和启动配置
├── BOOT-INF/
│ ├── classes/ # 应用代码和资源
│ │ ├── com/
│ │ │ └── example/
│ │ │ └── MyApplication.class
│ │ └── application.properties
│ └── lib/ # 依赖的所有 JAR 包
│ ├── spring-boot.jar
│ ├── spring-core.jar
│ └── ...
└── org/ # 嵌入式启动器类(如 org.springframework.boot.loader)
2. 核心启动机制
2.1 MANIFEST.MF 文件
可执行 JAR 的 MANIFEST.MF 中包含特殊配置:
manifest
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.MyApplication
- Main-Class:Spring Boot 自定义的启动类
JarLauncher,负责加载内部 JAR 和资源。 - Start-Class:应用的主入口类(即包含
main方法的类)。
2.2 自定义类加载器
Spring Boot 通过 LaunchedURLClassLoader 实现嵌套 JAR 的加载:
- 标准 JVM 限制:Java 原生类加载器无法直接加载嵌套在 JAR 中的 JAR(如
BOOT-INF/lib下的依赖)。 - 自定义加载器突破限制:
LaunchedURLClassLoader扩展了标准类加载器,能够从嵌套 JAR 中读取类和资源。
2.3 启动流程
- JVM 启动:执行
java -jar my-app.jar时,JVM 首先加载MANIFEST.MF中指定的Main-Class(即JarLauncher)。 - 嵌套 JAR 加载:
JarLauncher初始化LaunchedURLClassLoader,将BOOT-INF/lib下的所有依赖添加到类路径中。 - 应用主类启动:
JarLauncher反射调用Start-Class(即com.example.MyApplication)的main方法。 - Spring 上下文初始化:
main方法触发 Spring Boot 应用的启动流程,包括自动配置、组件扫描和嵌入式服务器启动。
3. 嵌入式服务器的集成
Spring Boot 的嵌入式服务器(如 Tomcat)以依赖形式存在于 BOOT-INF/lib 中:
- 服务器作为普通依赖:Tomcat/Jetty 被打包到 JAR 中,作为应用的一部分运行。
- 自动配置:Spring Boot 自动配置嵌入式服务器,无需独立安装和配置。
- 内嵌运行:应用启动时,嵌入式服务器直接在 JVM 内初始化并监听端口,无需外部容器。
4. 总结
1.SpringBoot 提供了一个插件 spring-boot-maven-plugin 用于把程序打包成一个可执行的 jar 包。
2.Spring Boot 应用打包之后,生成一个 Fat jar (jar 包中包含 jar),包含了应用依赖的 jar 包和 Spring Boot loader 相关的类。
3.java -jar 会去找 jar 中的 manifest 文件,在那里面找到真正的启动类(Main-Class);
4.Fat jar 的启动 Main 函数是 JarLauncher,它负责创建一个 LaunchedURLClassLoader 来加载 boot-lib 下面的 jar,并以一个新线程启动应用的启动类的 Main 函数(找到 manifest 中的 Start-Class)。