前置通俗铺垫:所有Java Web项目、接口服务、后台管理系统,都离不开Servlet容器。简单来说,Servlet容器就是项目的专属Web服务器,负责接收客户端HTTP请求、分发请求、执行业务逻辑、最终返回响应结果。没有Servlet容器,我们编写的Controller、接口、业务代码无法运行。

一、嵌入式Servlet容器背景与行业现状

1.1 嵌入式容器核心诞生背景

在SpringBoot问世前,传统SSM架构的Web项目均采用外置独立Servlet容器(外置Tomcat)部署。开发部署流程繁琐且痛点极多:需要单独下载、安装、配置Tomcat服务器,项目必须打包为War包,手动放置到Tomcat指定目录,还要统一配置端口、环境参数,多项目部署极易出现端口冲突、版本不兼容、环境不一致等问题。

为彻底解决传统部署的冗余问题,SpringBoot推出嵌入式Servlet容器核心设计:将Web服务器直接内置集成到项目内部,项目打包为Jar包后自带完整服务能力,无需单独安装服务器,仅依赖JDK即可一键启动

该设计是当下微服务架构、Docker容器化部署、云原生项目的核心基础,极大降低了开发、测试、部署、运维的整体成本,也是目前企业项目的主流部署方式。

1.2 SpringBoot官方支持嵌入式容器介绍

根据SpringBoot 2.x官方规范,框架原生标准化支持三款合规嵌入式Servlet容器,全部遵循Servlet官方规范,底层核心机制统一,仅在性能、资源占用、适配场景上存在差异,开发者可通过简单依赖替换,零业务代码修改完成容器切换。三款容器定位清晰、各司其职:

  • Tomcat 9.0(默认自带):适配Servlet 4.0规范,功能全面、兼容性极强、生态成熟,是中小型项目默认首选,主打稳定可靠。

  • Jetty 9.4/10.0(轻量专用):极简轻量化容器,低内存、快启动,对WebSocket长连接深度优化,主打轻量化部署、实时通信场景。

  • Undertow 2.0(高性能专用):基于异步非阻塞IO模型,高并发吞吐能力拉满,主打大流量、高并发核心业务场景。

同时SpringBoot官方兼容Servlet3.1及以上所有外置容器,支持嵌入式Jar部署、外置War部署双模式无缝切换,兼容性覆盖绝大多数生产场景。

1.3 三大嵌入式容器优缺点与适用场景汇总

1.3.1 Tomcat(通用稳定型)

核心优点:开箱即用无需额外配置,SpringBoot默认集成;行业生态最完善,报错解决方案、运维调优、集群方案全网最全;功能无短板,兼容JSP、Session持久化、静态资源、集群部署等所有传统Web特性;经过十几年生产验证,稳定性极强,故障概率极低。

核心缺点:资源开销高于Jetty和Undertow,启动速度慢、常驻内存占用偏大;基于阻塞线程模型,超高并发场景易出现线程阻塞、请求排队;热部署适配较差,开发调试重启耗时久。

适用场景:企业后台管理系统、常规CRUD业务项目、前后端分离普通项目、SSM老旧迁移项目、JSP传统项目,所有以稳定性为核心、无极致并发需求的业务系统。

1.3.2 Jetty(轻量长连接型)

核心优点:架构精简无冗余功能,包体极小、启动极速、内存占用极低,适配低配服务器;WebSocket长连接稳定性优于Tomcat,专为实时通信优化;原生支持热部署,开发调试效率高;原生适配嵌入式部署,无外置容器冗余逻辑。

核心缺点:异步并发性能弱于Undertow,超大流量场景存在性能瓶颈;运维生态较弱,集群调优、线上故障排查资料少于Tomcat;对老旧复杂JSP项目兼容性一般。

适用场景:实时消息推送、物联网设备长连接通信、轻量微服务、低配服务器部署、开发环境热调试、小型工具类Web服务。

1.3.3 Undertow(高性能高并发型)

核心优点:基于XIO异步非阻塞模型,少量线程支撑上万并发请求,吞吐量大、延迟低;CPU、内存资源利用率远高于Tomcat;原生支持HTTP/2、HTTPS、WebSocket等现代化协议;无锁设计、请求复用性好,高可用场景适配性极强。

核心缺点:官方明确不支持JSP,无法适配老旧JSP项目;异步模型线程栈不直观,线上异常、性能问题排查门槛高;中小厂落地经验少,运维人才储备不足。

适用场景:互联网高并发核心服务、API网关、秒杀抢购、订单支付、大数据量接口、响应式Web项目等需要极致吞吐性能的生产业务。

二、嵌入式Servlet容器常用配置(实战代码+详解)

SpringBoot摒弃了传统Tomcat的xml配置文件,提供配置文件配置+代码定制两种方式修改容器参数,全局通用配置适配所有容器,专属配置仅对对应容器生效,配置简单、优先级清晰,适配生产调优需求。

2.1 配置文件方式(application.yml 通用+专属配置)

该方式简单直观、无需改代码,适合常规参数调整,是企业最常用的配置方式,所有配置严格对齐SpringBoot官方ServerProperties配置类规范。

# 嵌入式Servlet容器通用配置(所有容器通用)
server:
  # 服务端口
  port: 8080
  # 项目访问根路径
  servlet:
    context-path: /demo
  # 连接超时时间
  connection-timeout: 20000
  # 开启响应压缩,提升接口传输效率
  compression:
    enabled: true
    min-response-size: 1024

# Tomcat专属定制配置
  tomcat:
    # 最大线程数
    max-threads: 200
    # 最小空闲线程数
    min-spare-threads: 20
    # 最大连接数
    max-connections: 10000
    # 编码格式
    uri-encoding: UTF-8
    # 开启访问日志
    basedir: ./tomcat-log

# Jetty专属配置
#  jetty:
#    max-threads: 150
#    min-threads: 10

# Undertow专属配置
#  undertow:
#    worker-threads: 200
#    buffer-size: 1024

2.2 代码定制方式(优先级高于配置文件)

通过实现WebServerFactoryCustomizer接口,可灵活定制容器参数,适合动态配置、复杂定制场景,优先级高于配置文件,会覆盖配置文件中的同名参数。

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

/**
 * 嵌入式Servlet容器自定义配置
 * 优先级高于yml/properties配置文件
 */
@Component
public class CustomServerConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        // 自定义端口
        factory.setPort(8088);
        // 自定义项目根路径
        factory.setContextPath("/custom");
        // 自定义会话超时时间
        factory.setSessionTimeout(3600);
    }
}

三、Servlet三大组件注册与全量实战(场景+代码+优缺点)

Servlet三大组件包含Servlet、Filter、Listener,是Java Web的核心基础组件。SpringBoot默认不自动扫描原生Servlet注解,需要手动开启扫描或通过Bean注册,两种方式各有适配场景,下面结合实战全覆盖讲解。

3.1 核心前置说明

SpringBoot提供两种注册三大组件的方式:注解扫描方式(简洁快速)、Bean手动注册方式(灵活可控),可根据业务场景自由选择。

3.2 Servlet组件(请求处理器)

3.2.1 使用场景

原生Servlet用于接收、处理特定路径的HTTP请求,适合自定义特殊请求逻辑、底层请求拦截处理、适配传统Web接口,SpringBoot中多用于自定义特殊接口、中间件请求转发场景。

3.2.2 实现方式1:注解方式

启动类添加@ServletComponentScan开启Servlet注解扫描,无需手动注册,简洁高效。

// 启动类开启Servlet组件扫描
@SpringBootApplication
@ServletComponentScan
public class ServletApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServletApplication.class, args);
    }
}

// 自定义Servlet
@WebServlet(urlPatterns = "/customServlet")
public class CustomServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().write("自定义Servlet请求处理成功!");
    }
}

3.2.3 实现方式2:Bean注册方式

无需开启注解扫描,通过ServletRegistrationBean手动注册,支持动态配置映射路径、启动优先级,灵活性更高。

@Configuration
public class ServletBeanConfig {
    @Bean
    public ServletRegistrationBean<CustomServlet> customServletBean() {
        ServletRegistrationBean<CustomServlet> bean = new ServletRegistrationBean<>();
        bean.setServlet(new CustomServlet());
        bean.addUrlMappings("/beanServlet");
        bean.setName("beanCustomServlet");
        return bean;
    }
}

3.2.4 优缺点

优点:底层请求处理能力强,可精准控制请求流程,适配特殊自定义请求场景,兼容性好;

缺点:编码繁琐,不如Controller简洁,不适合常规CRUD接口开发,仅适用于特殊底层场景。

3.3 Filter组件(请求过滤器)

3.3.1 使用场景

Filter是请求拦截器,在Servlet处理请求之前、响应返回之后执行,常用于统一编码设置、跨域处理、权限拦截、请求参数过滤、日志记录、黑名单拦截等全局通用操作。

3.3.2 实战代码

// 注解方式
@WebFilter(urlPatterns = "/*")
public class CustomFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 请求预处理:统一设置编码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        // 放行请求
        chain.doFilter(request, response);
        // 响应后处理逻辑
    }
}

// Bean注册方式
@Bean
public FilterRegistrationBean<CustomFilter> customFilterBean() {
    FilterRegistrationBean<CustomFilter> bean = new FilterRegistrationBean<>();
    bean.setFilter(new CustomFilter());
    bean.addUrlPatterns("/*");
    bean.setOrder(1); // 设置执行优先级,数值越小优先级越高
    return bean;
}

3.3.3 优缺点

优点:全局拦截生效,无需每个接口单独配置,适合统一全局处理逻辑,性能损耗极低;

缺点:拦截所有匹配路径,无法精准区分业务接口,精细化权限控制不如拦截器,仅适合全局通用操作。

3.4 Listener组件(事件监听器)

3.4.1 使用场景

Listener是Web事件监听组件,用于监听项目启动、销毁、Session创建销毁、请求创建销毁等事件,常用于项目初始化加载资源、项目关闭释放资源、在线用户统计、会话监控、全局参数初始化等场景。

3.4.2 实战代码

// 注解方式:监听项目启动与销毁
@WebListener
public class CustomListener implements ServletContextListener {
    // 项目启动时执行
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("项目启动成功,初始化全局资源!");
    }

    // 项目关闭时执行
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("项目关闭,释放全局资源!");
    }
}

// Bean注册方式
@Bean
public ServletListenerRegistrationBean<CustomListener> customListenerBean() {
    return new ServletListenerRegistrationBean<>(new CustomListener());
}

3.4.3 优缺点

优点:事件驱动、自动触发,无需手动调用,适合全局初始化、资源回收、监控统计场景;

缺点:监听粒度较粗,无法精准监听单个接口事件,仅适用于全局Web生命周期事件处理。

四、嵌入式容器切换实战(Tomcat/Jetty/Undertow)

SpringBoot容器切换零业务代码修改,仅需排除默认Tomcat依赖、引入目标容器依赖即可,操作极简,适配不同业务性能需求。

4.1 切换为Jetty容器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- 排除默认Tomcat容器 -->
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 引入Jetty容器依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

4.2 切换为Undertow容器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- 排除默认Tomcat容器 -->
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 引入Undertow容器依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

五、嵌入式Servlet容器自动配置与启动底层原理

5.1 整体流程总览

嵌入式Servlet容器从零启动的完整闭环,严格遵循以下4步顺序执行,缺一不可: 1. 自动配置类加载:SPI机制自动加载容器核心自动配置入口 2. 容器工厂动态择优创建:条件注解根据项目依赖,自动生成唯一容器工厂 3. 多层定制器配置覆盖:后置处理器批量执行定制器,完成容器参数优先级覆盖 4. 工厂实例化启动容器:统一工厂入口启动服务器,注册组件、监听端口

步骤一:SPI机制自动加载容器核心配置类

1.1 详细原理:SpringBoot启动初始化自动配置时,会扫描读取项目中所有 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,通过SPI扩展机制自动加载文件中注册的所有自动配置类。其中包含Web容器专属核心配置类 ServletWebServerFactoryAutoConfiguration,该类是所有嵌入式容器自动配置的唯一全局入口,无需开发者手动注册、无需开启任何注解,框架自动加载生效。

1.2 核心作用:统一容器自动配置入口,批量导入三款容器的配置模板,为后续容器工厂创建、参数绑定、服务启动提供底层支撑。

1.3 核心源码示例

// SpringBoot官方容器自动配置唯一入口类
@Configuration(proxyBeanMethods = false)
// 批量导入Tomcat、Jetty、Undertow三款容器的内置配置类
@Import({EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
// 绑定全局server配置,自动读取yml中server.port、server.context-path等参数
@EnableConfigurationProperties(ServerProperties.class)
public class ServletWebServerFactoryAutoConfiguration {
    // 无硬编码业务逻辑,仅作为自动配置入口,统一管理容器初始化流程
}

1.4 阶段小结:本步骤是容器零配置启动的前置基础,依靠SPI机制实现配置类的自动发现与加载,彻底摆脱传统Web项目手动配置容器的繁琐操作,实现开箱即用的核心能力。

步骤二:条件注解动态择优创建容器工厂

2.1 详细原理:上一步加载的三款容器内置配置类,均配置SpringBoot核心条件注解。SpringIoC容器扫描Bean时,会自动校验当前项目classpath依赖环境:仅当项目引入对应容器依赖、存在容器核心类时,该容器的配置类才会生效,全局只会实例化唯一一个容器工厂Bean,彻底杜绝多容器冲突问题,实现「依赖驱动容器切换」。

核心注解机制解析: - @ConditionalOnClass:判定classpath是否存在对应容器核心类,存在则配置类生效 - @ConditionalOnMissingBean:判定容器中无容器工厂Bean时才创建,保证全局唯一

2.2 核心作用:无需修改代码、无需手动配置,仅通过切换Maven依赖,即可自动择优创建对应容器工厂,实现容器无感切换。

2.3 完整核心源码示例

// 1、Tomcat容器配置类(SpringBoot默认生效)
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties properties) {
        return new TomcatServletWebServerFactory();
    }
}

// 2、Jetty容器配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedJetty {
    @Bean
    public JettyServletWebServerFactory jettyServletWebServerFactory() {
        return new JettyServletWebServerFactory();
    }
}

// 3、Undertow容器配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedUndertow {
    @Bean
    public UndertowServletWebServerFactory undertowServletWebServerFactory() {
        return new UndertowServletWebServerFactory();
    }
}

2.4 本步骤执行流程: 1. 项目启动扫描三款容器内置配置类; 2. 逐一校验classpath下的容器核心依赖类; 3. 匹配成功的配置类实例化对应容器工厂Bean; 4. 其余两款容器因条件不满足,自动失效不加载。

2.5 阶段小结:容器动态切换的核心本质就是条件注解的动态筛选机制,依托SpringBoot自动配置的解耦设计,实现零代码、零配置的容器切换,适配不同业务性能场景。

步骤三:多层定制器机制实现容器参数分层覆盖

3.1 详细原理:容器工厂Bean创建完成后,不会直接启动。SpringBoot通过Bean后置处理器+多层定制器机制,统一处理所有容器配置。所有配置按照固定优先级依次执行,后执行的高优先级配置会覆盖低优先级配置,最终完成端口、线程池、超时时间、SSL等参数的个性化定制,解决多配置冲突问题。

3.2 配置优先级(由低到高,逐级覆盖) 1. 全局通用定制器:绑定 server.* 全局通用配置(端口、上下文路径、超时) 2. 容器专属定制器:绑定 server.tomcat/server.jetty/server.undertow 容器独有配置 3. 自定义代码定制器:开发者手动编码配置,优先级最高,覆盖所有默认配置

3.3 核心执行载体WebServerFactoryCustomizerBeanPostProcessor 后置处理器,自动收集容器中所有定制器,在容器工厂初始化前统一回调执行。

3.4 核心源码+实战代码示例

3.4.1 后置处理器底层调度源码
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor {
    // 自动注入容器中所有生效的定制器
    private final List<WebServerFactoryCustomizer<?>> customizers;

    // 在容器工厂Bean初始化之前执行定制逻辑,完成参数覆盖
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 仅拦截Servlet容器工厂Bean,精准限定作用范围
        if (bean instanceof ConfigurableServletWebServerFactory) {
            // 循环执行所有定制器,按优先级批量覆盖参数
            for (WebServerFactoryCustomizer<?> customizer : this.customizers) {
                ((WebServerFactoryCustomizer) customizer).customize(bean);
            }
        }
        return bean;
    }
}
3.4.2 最高优先级自定义定制器(可直接运行)
@Component
public class CustomWebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        // 全局通用参数定制
        factory.setPort(8090);
        factory.setSessionTimeout(7200);
        factory.setContextPath("/server");

        // Tomcat容器专属个性化定制
        if (factory instanceof TomcatServletWebServerFactory) {
            TomcatServletWebServerFactory tomcatFactory = (TomcatServletWebServerFactory) factory;
            tomcatFactory.addAdditionalTomcatConnectors(createSslConnector());
        }
    }

    // 自定义HTTPS连接器,实现SSL配置
    private Connector createSslConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(443);
        return connector;
    }
}

3.5 阶段小结:项目中yml、properties、代码所有配置,最终都通过定制器机制统一生效。分层覆盖的设计完美兼顾了通用配置的简洁性和自定义配置的灵活性,是生产环境容器参数调优的核心底层原理。

步骤四:容器工厂实例化启动服务器

4.1 详细原理:所有参数定制完成后,容器工厂参数初始化完毕。SpringBoot通过统一工厂接口方法 getWebServer() 实例化原生容器,依次完成参数绑定、Web上下文创建、三大组件注册、端口监听,最终启动Web服务,对外提供HTTP请求处理能力。

4.2 完整启动分步逻辑 1. 初始化Tomcat/Jetty/Undertow原生容器实例; 2. 绑定所有定制后的端口、线程池、超时、编码等参数; 3. 创建Web运行上下文,预留组件注册环境; 4. 批量注册项目中所有Servlet、Filter、Listener组件; 5. 启动容器、监听端口、循环处理客户端HTTP请求。

4.3 核心启动源码示例(Tomcat完整版)

public class TomcatServletWebServerFactory implements ServletWebServerFactory {
    // 所有嵌入式容器统一启动入口
    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        // 1. 初始化原生Tomcat实例
        Tomcat tomcat = new Tomcat();
        
        // 2. 绑定定制后的端口、协议、连接器参数
        Connector connector = new Connector(getProtocol());
        connector.setPort(getPort());
        tomcat.getService().addConnector(connector);
        
        // 3. 创建Web上下文,配置项目访问根路径
        StandardContext context = new StandardContext();
        context.setPath(getContextPath());
        tomcat.addContext(context);
        
        // 4. 批量注册Servlet、Filter、Listener三大组件
        for (ServletContextInitializer initializer : initializers) {
            initializer.onStartup(context.getServletContext());
        }
        
        // 5. 启动Tomcat容器,监听端口,对外提供服务
        tomcat.start();
        return new TomcatWebServer(tomcat, getPort() >= 0);
    }
}

4.4 阶段小结:工厂模式统一了三款容器的上层启动入口,屏蔽了底层不同容器的实现差异,实现上层统一调用、底层差异化适配的高解耦架构,这也是SpringBoot容器可无缝切换的核心原因。

5.2 嵌入式容器整体原理总结

SpringBoot嵌入式容器零配置启动、可动态切换、可灵活定制的核心本质,是四大机制协同作用的结果: 1. SPI自动加载机制:自动加载容器核心配置类,实现零手动配置; 2. 条件注解择优机制:依赖驱动容器创建,实现无代码容器切换; 3. 多层定制器覆盖机制:分级管理配置,兼顾通用性与个性化调优; 4. 工厂统一启动机制:屏蔽底层差异,统一服务启动流程。

整体闭环链路:自动加载配置类 → 动态创建容器工厂 → 分层定制覆盖参数 → 统一工厂启动服务,完全适配微服务、容器化、云原生的现代化部署架构。

六、外置Servlet容器部署与底层启动原理(分步拆解+源码详解)

外置Tomcat容器与嵌入式容器启动逻辑完全不同,不执行SpringBoot原生main方法,全程依托Servlet3.0 SPI扩展规范实现项目初始化启动。本章同样采用分步拆解模式,逐步骤讲解原理、配套核心源码、阶段复盘,最后汇总整体闭环逻辑。

6.1 外置容器定义、优势与适用场景

外置Servlet容器:独立于项目之外、单独安装部署的独立Tomcat服务器,项目打包为War包部署至外置Tomcat运行,项目本身不携带任何Web服务器依赖。

核心特点:容器进程与业务进程分离、支持多项目共用同一容器、适配老旧JSP/War项目、支持统一集群运维管控。

适用场景:老旧War/JSP项目迁移、企业统一Tomcat集群运维、服务器禁止业务自带端口的管控场景、传统Java Web集群架构项目。

行业现状:微服务、Docker、云原生项目均使用嵌入式容器,外置容器仅用于老旧系统维护,新项目不再推荐使用。

6.2 SpringBoot项目适配外置容器标准化改造步骤

SpringBoot项目适配外置Tomcat部署,必须完成三步标准化改造,缺一不可:

6.2.1 修改项目打包方式为War

<!-- pom.xml 全局修改打包方式 --> <packaging>war</packaging>

6.2.2 内置Tomcat依赖设置为provided

<!-- 排除内置Tomcat,避免与外置Tomcat冲突 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

scope=provided含义:编译、测试阶段依赖生效,打包运行时不携带该依赖,由外置Tomcat容器提供运行环境。

6.2.3 自定义外置启动适配类

外置Tomcat无main方法,无法通过原生方式启动SpringBoot项目,需继承 SpringBootServletInitializer 适配Servlet3.0启动规范,绑定项目主启动类。

/**
 * 外置Tomcat专属启动适配类
 * 替代main方法,供外置容器回调初始化Spring上下文
 */
public class OuterTomcatStartApplication extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        // 绑定项目真实主启动类,完成SpringBoot上下文初始化
        return builder.sources(ServletContainerApplication.class);
    }
}

6.3 外置容器启动核心底层原理(四步分步拆解)

外置容器启动核心:依托Servlet3.0 SPI扩展规范,实现无web.xml、无main方法启动SpringBoot项目,完整流程分为4个核心步骤。

步骤一:外置Tomcat启动扫描SPI配置文件

1.1 详细原理:Servlet3.0规范摒弃了传统web.xml配置,定义SPI扩展机制:外置Tomcat启动时,会自动扫描所有Jar包下的 META-INF/services/javax.servlet.ServletContainerInitializer 文件,自动加载文件中配置的容器初始化器,实现容器与框架的自动适配。

1.2 核心作用:让外置Tomcat具备识别SpringBoot项目的能力,打通容器与项目的启动关联。

1.3 核心说明:Spring框架已预先在核心Jar中配置SPI文件,固定写入 org.springframework.web.SpringServletContainerInitializer,Tomcat启动自动加载该类,无需开发者手动配置。

1.4 阶段小结:SPI机制是外置容器启动SpringBoot项目的前置基础,依靠官方规范实现自动适配,无需手动配置启动入口。

步骤二:SPI初始化器扫描项目启动适配类

2.1 详细原理:Tomcat加载 SpringServletContainerInitializer 后,该类通过 @HandlesTypes(WebApplicationInitializer.class) 注解,自动扫描项目中所有 WebApplicationInitializer 接口的实现类,我们自定义的外置启动适配类正是该接口的实现类,会被精准扫描捕获。

2.2 核心作用:精准定位项目自定义启动入口,为后续回调初始化方法做准备。

2.3 核心底层源码

// Spring官方SPI核心初始化类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    // Tomcat启动自动回调该方法
    @Override
    public void onStartup(Set<Class<?>> initializerClasses, ServletContext servletContext) {
        // 实例化所有WebApplicationInitializer实现类
        List<WebApplicationInitializer> initializers = new ArrayList<>();
        for (Class<?> clazz : initializerClasses) {
            if (!clazz.isInterface()) {
                initializers.add((WebApplicationInitializer) clazz.getDeclaredConstructor().newInstance());
            }
        }
        // 批量回调初始化方法
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }
}

2.4 阶段小结:通过注解绑定接口的扫描机制,让外置容器可以自动发现项目启动适配类,彻底脱离main方法启动限制。

步骤三:回调适配类,绑定SpringBoot主启动类

3.1 详细原理:SPI初始化器扫描到自定义适配类后,自动回调其 onStartup 方法,进而触发重写的 configure 方法,绑定项目真实的SpringBoot主启动类,告知容器项目的核心初始化入口。

3.2 核心作用:完成外置Tomcat与SpringBoot项目的绑定,替代原生main方法的启动功能。

3.3 阶段小结:该步骤是SpringBoot适配外置容器的核心适配逻辑,是两种部署模式启动入口差异的关键所在。

步骤四:初始化IoC容器,完成项目启动

4.1 详细原理:绑定主启动类后,SpringBoot自动执行IoC容器初始化、加载所有配置类、注册所有Bean与Servlet三大组件、初始化Web环境,最终完成项目启动,外置Tomcat开始监听端口、处理客户端HTTP请求。

4.2 阶段小结:项目启动后的Bean加载、组件注册、请求处理逻辑,与嵌入式容器完全一致,仅启动入口不同。

6.4 外置容器整体原理总结

完整闭环启动链路:外置Tomcat启动 → 扫描Servlet3.0 SPI初始化器 → 加载SpringServletContainerInitializer → 扫描项目WebApplicationInitializer实现类 → 回调configure方法绑定主类 → 初始化SpringIoC容器 → 项目启动、监听端口。

核心本质差异: 1. 嵌入式容器:main方法+工厂机制 驱动启动,Jar包独立运行,自带容器; 2. 外置容器:Servlet3.0 SPI接口回调 驱动启动,War包依赖外置容器运行。

面试核心结论:SpringBoot项目支持外置Tomcat部署的根本原因,是适配了Servlet3.0 SPI扩展规范,让外置容器可自动回调项目初始化入口,无需依赖main方法即可完成项目启动。

七、全文核心考点总结

1、嵌入式容器核心设计:依托SPI自动配置、条件注解择优、多层定制器配置、工厂统一启动,实现零配置、可切换、高灵活的Web服务部署;

2、容器切换原理:纯Maven依赖驱动,条件注解动态筛选生效容器,零业务代码修改;

3、配置优先级:自定义代码配置 > 容器专属配置 > 全局通用配置,后置处理器统一调度生效; 4、三大组件:支持注解扫描、Bean注册双模式,分别负责请求处理、全局拦截、事件监听,适配不同Web场景;

5、外置容器核心:基于Servlet3.0 SPI机制启动,适配老旧War项目,与嵌入式容器核心差异为启动入口不同;

6、行业选型规范:新项目、微服务、云原生优先使用嵌入式容器,老旧遗留项目维护使用外置容器。

特殊备注:该文中的示例代码为源代码整理后抽取部分核心点的代码作为理解展示,非真实源码,要看源码请自行根据方法名/类名前往对应的源码文件查看

更多推荐