SpringBoot Servlet 容器全解析:嵌入式配置与外置容器部署

在 SpringBoot Web 开发中,Servlet 容器是核心基础设施。SpringBoot 提供了两种容器使用方式:嵌入式容器(默认)和外置容器,前者便捷轻量,后者适配传统 Web 场景(如 JSP 开发)。本文将从配置、实战、原理三个维度,完整讲解嵌入式容器的定制、Servlet 三大组件注册,以及外置容器的部署流程与底层逻辑。

一、嵌入式 Servlet 容器(Jar 包方式)

嵌入式容器是 SpringBoot 的默认方式,将 Tomcat/Jetty/Undertow 内置到应用中,打成可执行 Jar 包,通过java -jar即可启动。核心优势是简单、便携,但默认不支持 JSP,精细化定制需额外配置。

1.1 定制 Servlet 容器核心配置

SpringBoot 通过ServerProperties类封装所有容器配置,支持配置文件和编程式两种方式修改,最常用的是配置文件方式。

1.1.1 配置文件定制(application.properties/yml)

直接在配置文件中通过server前缀修改容器参数,覆盖默认值:

复制代码
# 基础容器配置
server.port=8081                          # 服务端口(默认8080)
server.context-path=/tx                   # 应用上下文路径(SpringBoot2.0后推荐server.servlet.context-path)
server.tomcat.uri-encoding=UTF-8          # Tomcat URI编码(解决中文乱码)

# Tomcat精细化配置(可选)
server.tomcat.max-threads=200             # 最大工作线程数
server.tomcat.connection-timeout=20000    # 连接超时时间(毫秒)

若使用 YAML 格式,配置更易读:

XML 复制代码
server:
  port: 8081
  servlet:
    context-path: /tx
  tomcat:
    uri-encoding: UTF-8
    max-threads: 200
    connection-timeout: 20000
1.1.2 核心配置说明
  • server.port:指定访问端口,避免端口冲突;
  • server.context-path:设置访问前缀,配置后访问地址为http://localhost:8081/tx/xxx
  • server.tomcat.*:Tomcat 专属配置,SpringBoot 对 Jetty/Undertow 也提供了对应的前缀(如server.jetty.*)。

1.2 注册 Servlet 三大组件(无 web.xml)

SpringBoot 以 Jar 包启动时无web.xml文件,需通过@Bean注解将 Servlet、Filter、Listener 注册到容器中。

1.2.1 先定义自定义组件
① 自定义 Servlet
java 复制代码
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("自定义Servlet响应");
    }
}
② 自定义 Filter
java 复制代码
import javax.servlet.*;
import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("MyFilter拦截请求...");
        chain.doFilter(request, response); // 放行请求
    }
}
③ 自定义 Listener
java 复制代码
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("Servlet容器初始化完成");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Servlet容器销毁");
    }
}
1.2.2 注册组件到 Spring 容器

创建配置类,通过ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean封装组件并注册:

java 复制代码
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;

@Configuration
public class WebComponentConfig {

    // 1. 注册Servlet
    @Bean
    public ServletRegistrationBean<MyServlet> myServlet() {
        ServletRegistrationBean<MyServlet> registrationBean = new ServletRegistrationBean<>(
                new MyServlet(), "/myServlet"); // 拦截路径
        return registrationBean;
    }

    // 2. 注册Filter
    @Bean
    public FilterRegistrationBean<MyFilter> myFilter() {
        FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MyFilter());
        registrationBean.setUrlPatterns(Arrays.asList("/hello", "/myServlet")); // 拦截路径
        return registrationBean;
    }

    // 3. 注册Listener
    @Bean
    public ServletListenerRegistrationBean<MyListener> myListener() {
        ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(
                new MyListener());
        return registrationBean;
    }
}

1.3 DispatcherServlet 的自动注册

SpringBoot 自动配置 SpringMVC 时,会通过DispatcherServletAutoConfiguration自动注册核心前端控制器DispatcherServlet,核心逻辑如下:

java 复制代码
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
    ServletRegistrationBean registration = new ServletRegistrationBean(
            dispatcherServlet, this.serverProperties.getServletMapping());
    // 默认拦截路径:/ (拦截所有请求,包含静态资源,但不拦截JSP)
    // /* 会拦截JSP,开发中避免使用
    registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
    return registration;
}
  • 默认拦截路径为/,可通过server.servletPath修改;
  • 无需手动注册,SpringBoot 自动完成。

二、外置 Servlet 容器(War 包方式)

嵌入式容器虽便捷,但存在默认不支持 JSP、定制复杂等问题。外置容器需独立安装 Tomcat,将应用打成 War 包部署,适配传统 Web 开发场景。

2.1 嵌入式 VS 外置容器对比

特性 嵌入式容器(Jar) 外置容器(War)
打包方式 可执行 Jar 包 War 包
JSP 支持 默认不支持 原生支持
定制难度 较高(编程式) 较低(容器配置)
部署便捷性 极高(java -jar) 需先启动容器
适用场景 微服务、小型应用 传统 Web、JSP 开发

2.2 外置 Tomcat 部署完整步骤

以 IDEA + Maven 为例,演示从项目创建到部署的全流程:

步骤 1:创建 War 类型项目
  • IDEA 新建 SpringBoot 项目时,Packaging 选择 War
  • 若已有 Jar 项目,修改pom.xml<packaging>jar</packaging><packaging>war</packaging>
步骤 2:排除嵌入式 Tomcat

将嵌入式 Tomcat 标记为provided(编译时依赖,运行时由外置容器提供),避免冲突:

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>
步骤 3:编写 SpringBootServletInitializer 子类

外置容器无法执行main方法,需通过此类桥接 SpringBoot 应用:

java 复制代码
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        // 传入SpringBoot主程序类
        return application.sources(SpringBoot04WebJspApplication.class);
    }
}
步骤 4:规范项目目录结构

War 项目需符合 Java EE 标准结构,核心目录如下:

复制代码
spring-boot-war-demo/
├── src/main/java/          # 源码目录
├── src/main/resources/     # 配置文件目录
└── src/main/webapp/        # Web根目录(Jar项目无此目录)
    └── WEB-INF/            # 受保护目录
        └── jsp/            # JSP页面目录(可选)
步骤 5:部署并启动外置 Tomcat
  1. IDEA 中配置本地 Tomcat:「Add Configuration」→ 「Tomcat Server」→ 「Local」;
  2. 切换到「Deployment」标签,添加当前项目的 War 包;
  3. 设置「Application context」(访问前缀),启动 Tomcat 即可。

2.3 启动原理对比

① Jar 包(嵌入式容器)启动流程
复制代码
执行main方法 → 启动SpringApplication → 创建IOC容器 → 初始化嵌入式Tomcat → 启动容器并提供服务

核心:应用主导,SpringBoot 启动时先创建 IOC 容器,再启动容器。

② War 包(外置容器)启动流程
复制代码
启动外置Tomcat → 扫描webapps目录下的War包 → 触发SpringBootServletInitializer → 加载SpringBoot主类 → 创建IOC容器 → Tomcat提供服务

核心:容器主导 ,Tomcat 启动后,通过SpringBootServletInitializer加载 SpringBoot 应用。

三、总结

核心要点

  1. 嵌入式容器
    • 配置文件通过server.*定制端口、上下文路径等参数;
    • 无 web.xml 时,通过RegistrationBean注册 Servlet/Filter/Listener;
    • DispatcherServlet 由 SpringBoot 自动注册,默认拦截/路径。
  2. 外置容器
    • 需创建 War 项目,排除嵌入式 Tomcat,编写SpringBootServletInitializer子类;
    • 启动逻辑为 "容器主导",适配 JSP、传统 Web 部署场景。
  3. 场景选择
    • 微服务、无 JSP 场景优先用嵌入式容器(Jar 包);
    • 需 JSP、精细化定制容器时用外置容器(War 包)。

通过本文的配置和原理讲解,你可根据实际场景灵活选择 Servlet 容器方式,既享受 SpringBoot 的便捷性,又能适配传统 Web 开发需求。

相关推荐
李少兄2 小时前
解决 org.springframework.context.annotation.ConflictingBeanDefinitionException 报错
java·spring boot·mybatis
BYSJMG2 小时前
计算机毕业设计选题推荐:基于Hadoop的城市交通数据可视化系统
大数据·vue.js·hadoop·分布式·后端·信息可视化·课程设计
BYSJMG2 小时前
Python毕业设计选题推荐:基于大数据的美食数据分析与可视化系统实战
大数据·vue.js·后端·python·数据分析·课程设计·美食
东东5162 小时前
OA自动化居家办公管理系统 ssm+vue
java·前端·vue.js·后端·毕业设计·毕设
没有bug.的程序员2 小时前
Spring Cloud Alibaba:Nacos 配置中心与服务发现的工业级深度实战
java·spring boot·nacos·服务发现·springcloud·配置中心·alibaba
程序员鱼皮3 小时前
前特斯拉 AI 总监:AI 编程最大的谎言,是 “提效”
前端·后端·ai·程序员·开发
一路向北⁢3 小时前
Spring Boot 3 整合 SSE (Server-Sent Events) 企业级最佳实践(二)
java·数据库·spring boot·sse·通信
好好研究4 小时前
SpringBoot使用外置Tomcat
spring boot·后端·tomcat
lynnlovemin4 小时前
云原生提速秘籍:Spring Boot转Spring Native实战指南
spring boot·spring·云原生·spring native