Spring Boot 内嵌容器原理是什么?jar/war 如何打包与部署?
结论先行:Spring Boot 把 Tomcat/Jetty/Undertow 作为库“内嵌”进应用,由 ServletWebServerFactory 创建并启动容器,因此 java -jar 即可运行。默认打可执行 jar(内含 BOOT-INF/classes 与 BOOT-INF/lib 的嵌套 jar,由 JarLauncher 引导);需要外部 Tomcat 时改打 war 并让主类继承 SpringBootServletInitializer。
一、内嵌容器原理
| 角色 | 说明 |
|---|---|
| ServletWebServerFactory | 工厂接口,决定创建哪种容器 |
| TomcatServletWebServerFactory | 默认实现(依赖 spring-boot-starter-web 自带 tomcat) |
| WebServer | 容器的抽象,start() 即监听端口 |
| ServletContextInitializer | 向容器注册 DispatcherServlet 等 |
- 依赖检测:classpath 存在 tomcat-embed-core 就自动装配 Tomcat;引入 jetty/undertow 替代 starter 即可换容器;
- 生命周期:SpringApplication.run 刷新上下文后创建并 start WebServer,端口可配 server.port;
- 想换容器:排除 spring-boot-starter-tomcat,加 spring-boot-starter-jetty 或 spring-boot-starter-undertow。
二、可执行 jar(默认,Boot 3 规范)
app.jar
├─ BOOT-INF/classes/ # 应用类与资源
├─ BOOT-INF/lib/ # 全部依赖 jar(嵌套)
├─ META-INF/MANIFEST.MF # Main-Class: JarLauncher
└─ org/springframework/boot/loader/
- 原理:JVM 不支持直接加载 jar 里的 jar,Boot 提供自定义 ClassLoader(LaunchedURLClassLoader)解出嵌套 jar;
- 启动入口 Main-Class 为 JarLauncher,它定位 BOOT-INF 后委托到应用自己的 main;
- 由 spring-boot-maven-plugin 的 repackage goal 在 package 阶段重打包生成。
三、war 部署外部容器
- pom 中
<packaging>war</packaging>; - 主类继承 SpringBootServletInitializer 并重写 configure:
@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(DemoApplication.class);
}
}
- 内嵌 Tomcat 依赖设为 provided(spring-boot-starter-tomcat),避免与外部容器冲突;
- 产物仍可直接
java -jar运行,也可丢进外部 Tomcat 的 webapps。
常见追问 / 记忆点
- 追问:为什么能 java -jar 运行且不依赖机器装 Tomcat?答:容器以库形式内嵌 + 自定义加载器读取嵌套 jar + JarLauncher 引导。
- 追问:容器端口/线程池在哪配?答:server.port、server.tomcat.threads.max 等 server.* 属性。
- 记忆点:内嵌 = 容器进依赖;jar 走 JarLauncher,war 走 SpringBootServletInitializer。