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 部署外部容器

  1. pom 中 <packaging>war</packaging>
  2. 主类继承 SpringBootServletInitializer 并重写 configure:
@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(DemoApplication.class);
    }
}
  1. 内嵌 Tomcat 依赖设为 provided(spring-boot-starter-tomcat),避免与外部容器冲突;
  2. 产物仍可直接 java -jar 运行,也可丢进外部 Tomcat 的 webapps。

常见追问 / 记忆点

  • 追问:为什么能 java -jar 运行且不依赖机器装 Tomcat?答:容器以库形式内嵌 + 自定义加载器读取嵌套 jar + JarLauncher 引导。
  • 追问:容器端口/线程池在哪配?答:server.port、server.tomcat.threads.max 等 server.* 属性。
  • 记忆点:内嵌 = 容器进依赖;jar 走 JarLauncher,war 走 SpringBootServletInitializer。
笔记加载中…