Spring Boot Web开发与内嵌容器详解
定位:第 05 篇,讲透内嵌容器机制与定制、Web 相关自动配置、静态资源、错误处理与常见 Web 定制
适用版本:Spring Boot 3.x(JDK 17+)
目录
一、内嵌容器
1.1 机制
spring-boot-starter-web 默认引入内嵌 Tomcat 。Boot 的 ServletWebServerFactoryAutoConfiguration 在容器刷新的 onRefresh 阶段(FW-01 refresh 第⑨步)创建并启动内嵌 Servlet 容器,注册 DispatcherServlet。
refresh → onRefresh
→ createWebServer():创建 Tomcat 实例、配置连接器
→ 注册 DispatcherServlet、过滤器
→ 启动监听
1.2 切换容器
默认:Tomcat
换 Undertow:排除 spring-boot-starter-tomcat,引入 spring-boot-starter-undertow
换 Jetty:同理换 jetty
响应式(WebFlux):默认 Netty
1.3 定制容器的三种方式
① 属性:server.port / server.servlet.context-path / server.tomcat.threads.max
② WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>(编程式)
③ 直接自定义 Servlet/Filter/Listener 注册(ServletRegistrationBean 等)
二、Web 自动配置
WebMvcAutoConfiguration(WB 篇细讲机制)默认提供:
| 装配 | 说明 |
|---|---|
| DispatcherServlet | 前端控制器,映射 / |
| 消息转换器 | Jackson(JSON)、String、表单等 |
| 静态资源处理 | 映射 classpath 资源到 /** |
| 欢迎页/错误页 | 默认行为 |
| 格式化 | 日期、数字格式化支持 |
定制而不覆盖 :想加拦截器/改格式化,实现 WebMvcConfigurer(加配置),而不是 @EnableWebMvc(后者关闭 Boot 默认自动配置,全部手写------除非确有必要不要用)。
三、静态资源与模板
3.1 静态资源
默认从这些位置服务静态文件,映射到 /**:
classpath:/static/、classpath:/public/、classpath:/resources/、classpath:/META-INF/resources/
src/main/resources/static/app.js → http://host/app.js
3.2 模板引擎
引入对应 starter(如 Thymeleaf),模板放 classpath:/templates/,由自动配置装配视图解析:
@GetMapping("/page")
String page(Model m) { m.addAttribute("name", "x"); return "index"; }
→ 渲染 templates/index.html
前后端分离项目通常不用模板引擎,纯 REST + 前端工程。
四、错误处理
4.1 默认机制
未捕获异常/404 默认转发到 /error,返回状态码 + 默认错误体(HTML 或 JSON)。
4.2 全局异常处理(主流做法)
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BizException.class)
public ResponseEntity<ErrorBody> biz(BizException e) {
return ResponseEntity.badRequest().body(new ErrorBody(e.getCode(), e.getMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorBody> fallback(Exception e) {
return ResponseEntity.status(500).body(ErrorBody.internal());
}
}
此处强调:生产必须有全局兜底,避免堆栈直接暴露。
4.3 自定义错误页/控制器
静态错误页放 src/main/resources/error/404.html;深度定制实现 ErrorController。
五、常见定制
5.1 JSON 定制
# 属性方式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.property-naming-strategy=SNAKE_CASE
# Bean 方式(更灵活):自定义 ObjectMapper 或 Jackson2ObjectMapperBuilderCustomizer
5.2 文件上传大小
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=20MB
5.3 优雅停机
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
停机时停止接收新请求、等待在途请求完成(超时强制关闭)------发布零中断的关键(呼应无损上下线)。
5.4 CORS
java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry r) {
r.addMapping("/api/**").allowedOrigins("https://web.example.com");
}
}
六、总结
- 内嵌容器:starter-web 默认 Tomcat,在 refresh 的 onRefresh 阶段创建启动;可换 Undertow/Jetty;定制走属性或 WebServerFactoryCustomizer。
- Web 自动配置:DispatcherServlet、消息转换、静态资源、默认 MVC 行为;用 WebMvcConfigurer 增量定制,勿用 @EnableWebMvc 覆盖默认。
- 静态/模板:静态资源默认映射若干 classpath 目录;模板引擎走 starter + templates/。
- 错误处理:默认 /error;生产用 @RestControllerAdvice 全局兜底,避免堆栈外泄。
- 高频定制:Jackson、multipart 大小、优雅停机(server.shutdown=graceful)、CORS。
七、常见高频面试题
1. Spring Boot 如何启动内嵌 Tomcat?
要点:starter-web 引入内嵌 Tomcat 依赖;应用启动刷新上下文时,ServletWebServerFactoryAutoConfiguration 在 onRefresh 阶段(refresh 第⑨步)通过 ServletWebServerFactory 创建 Tomcat 实例、配置连接器,注册 DispatcherServlet 与过滤器,然后启动监听端口。这改变了"应用部署进外部容器"的传统模型,变为容器随应用走,java -jar 直接运行。
2. 怎么切换内嵌容器(如换成 Undertow)?
要点:排除默认的 Tomcat 依赖再引入目标容器。Maven 中在 spring-boot-starter-web 里 exclude spring-boot-starter-tomcat,然后引入 spring-boot-starter-undertow(或 jetty)。Boot 的自动配置按 classpath 上存在的容器选择对应工厂。响应式应用(WebFlux)默认用 Netty。切换主要为了性能特性或团队偏好,功能上通常无差异。
3. @EnableWebMvc 和 WebMvcConfigurer 的区别?Boot 里用哪个?
要点:@EnableWebMvc 会完全接管 MVC 配置,关闭 Spring Boot 的 WebMvcAutoConfiguration 默认行为(静态资源、默认转换器、格式化等都没了),需要全部手写,除非要完全控制否则不要用。WebMvcConfigurer 是增量定制接口------实现它的方法(加拦截器、改 CORS、配格式化)叠加在 Boot 默认配置之上,保留默认便利。Boot 项目标准做法是实现 WebMvcConfigurer。
4. Spring Boot 默认如何处理静态资源?
要点:自动配置把 classpath 下的 /static/、/public/、/resources/、/META-INF/resources/ 目录映射到 /** 路径对外服务,如 static/app.js 可通过 /app.js 访问。可通过属性自定义位置与映射(spring.web.resources)。对前后端分离项目,静态资源常由前端服务器/CDN 托管,Boot 只提供 API。
5. Spring Boot 如何做全局异常处理?
要点:主流是 @RestControllerAdvice(@ControllerAdvice + @ResponseBody)定义全局异常处理器,用 @ExceptionHandler 按异常类型映射为统一错误响应(状态码 + 错误体),覆盖业务异常、参数校验异常、兜底 Exception。它替代默认的 /error 白页,避免堆栈信息外泄,统一错误格式供前端处理。也可配静态错误页或自定义 ErrorController。
6. 如何定制 Jackson 的 JSON 序列化(如日期格式)?
要点:简单场景用属性:spring.jackson.date-format、property-naming-strategy 等。复杂场景定义 Bean:自定义 ObjectMapper 覆盖默认,或更推荐实现 Jackson2ObjectMapperBuilderCustomizer(在默认基础上增量定制,保留 Boot 默认模块)。注意直接 new ObjectMapper 作 Bean 会丢失 Boot 的默认配置,应谨慎。
7. 什么是优雅停机?怎么开启?
要点:优雅停机在关闭时先停止接收新请求,等待在途请求处理完(有超时上限)再关闭,避免发布期间用户请求被硬断。开启:server.shutdown=graceful,配合 spring.lifecycle.timeout-per-shutdown-phase 设等待上限。配合负载均衡摘流与容器编排的停机钩子,实现发布零中断。
8. 文件上传大小限制怎么配?
要点:默认上限较小(单文件与总请求各有限制),需显式调大:spring.servlet.multipart.max-file-size(单文件)、spring.servlet.multipart.max-request-size(整请求)。同时注意反向代理(Nginx client_max_body_size)与内嵌容器层的限制,任一层卡住都会失败。大文件建议对象存储直传而非经应用转发。
9. WebMvcAutoConfiguration 都自动装配了什么?
要点:主要包括------DispatcherServlet(映射 /)、HttpMessageConverter(Jackson JSON、String、表单等)、静态资源处理与欢迎页、默认错误处理基础、日期/数字格式化支持、表单编码等。这些构成"零配置即可用的 Web 层"。理解它有助于解释为什么引入 starter-web 就能直接写 @RestController 返回 JSON。
10. 为什么生产要配置全局异常兜底?
要点:没有兜底时未捕获异常走默认 /error,可能暴露堆栈与内部信息(安全风险),且错误格式不统一让前端无法可靠处理。全局 @RestControllerAdvice 统一捕获并转换为约定的错误响应(状态码 + 错误码 + 消息),区分业务异常与系统异常,系统异常记日志告警但不回传细节。这是接口规范与安全的基本要求。
