深入 Spring MVC 底层:从 DispatcherServlet 到自定义组件的全链路解析 - 实践
Spring MVC作为Spring生态的核心Web框架,其设计哲学源于经典的MVC模式,但通过创新的组件化设计实现了高度解耦。其核心架构包含三大核心组件:
- DispatcherServlet:前端控制器,承担请求分发中枢角色
- HandlerMapping:请求路由解析器,实现URL到处理方法的精准映射
- HandlerAdapter:处理器适配器,统一不同类型处理器的调用方式
这三个组件构成请求处理的核心管道,配合拦截器、视图解析器等辅助组件,形成完整的请求生命周期管理
一、Spring MVC 核心架构:DispatcherServlet 的 “中央调度” 模式
Spring MVC 的核心设计模式是前端控制器模式(Front Controller Pattern),而 DispatcherServlet 正是这个模式的实现者。它承担了请求分发、组件协调的核心职责,是整个 Spring MVC 框架的 “大脑”。
1.1 DispatcherServlet 的初始化流程
在我们的配置类MvcConfig
中,通过@Bean
注解显式定义了 DispatcherServlet:
@Bean
public DispatcherServlet dispatcherServlet() {
log.info("Creating DispatcherServlet instance");
return new DispatcherServlet();
}
当 Spring 容器初始化时,DispatcherServlet 会经历以下关键阶段:
- 构造阶段:创建 DispatcherServlet 实例,此时尚未初始化核心组件
- 初始化阶段:容器调用
init()
方法,触发onRefresh()
钩子函数 - 策略初始化:在
onRefresh()
中调用initStrategies()
,初始化九大核心组件
九大核心策略组件及其作用
组件类型 | 核心作用 | 默认实现 | 自定义扩展点 |
---|---|---|---|
MultipartResolver | 处理文件上传请求 | StandardServletMultipartResolver | 可实现自定义文件上传逻辑 |
HandlerMapping | 请求 URL 到处理器的映射 | RequestMappingHandlerMapping | 自定义 URL 匹配规则 |
HandlerAdapter | 调用处理器方法的适配器 | RequestMappingHandlerAdapter | 支持特殊参数类型解析 |
ViewResolver | 逻辑视图名到物理视图的解析 | InternalResourceViewResolver | 集成 Thymeleaf、Freemarker 等 |
LocaleResolver | 处理国际化资源 | AcceptHeaderLocaleResolver | 基于 Cookie/Session 的国际化 |
ThemeResolver | 管理应用主题 | FixedThemeResolver | 动态主题切换 |
HandlerExceptionResolver | 统一异常处理 | ExceptionHandlerExceptionResolver | 全局异常处理策略 |
RequestToViewNameTranslator | 自动生成视图名 | DefaultRequestToViewNameTranslator | 自定义视图名生成规则 |
FlashMapManager | 跨请求数据传递 | SessionFlashMapManager | 分布式环境下的 Flash 数据管理 |
1.2 DispatcherServlet 的注册机制
创建 DispatcherServlet 实例后,还需要将其注册到 Servlet 容器中,这一过程由DispatcherServletRegistrationBean
完成:
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(
DispatcherServlet servlet, WebMvcProperties mvcProperties) {
DispatcherServletRegistrationBean bean =
new DispatcherServletRegistrationBean(servlet, "/");
bean.setLoadOnStartup(mvcProperties.getServlet().getLoadOnStartup());
return bean;
}
这里有两个关键配置:
- URL 映射路径:
"/"
表示拦截所有请求(除了 JSP 请求,由 Tomcat 默认 Servlet 处理) - 加载顺序:
loadOnStartup
值决定 Servlet 的初始化时机,正数表示容器启动时初始化,数值越小优先级越高
二、请求映射的底层实现:RequestMappingHandlerMapping
请求映射是 Spring MVC 的核心功能之一,用户通过@GetMapping
、@PostMapping
等注解定义的接口,最终都由RequestMappingHandlerMapping
来管理和匹配。
2.1 请求映射的注册过程
在DispatcherServletInitCase
的调试代码中,我们可以看到请求映射的注册详情:
RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class)
;
mapping.getHandlerMethods().forEach((requestMappingInfo, handlerMethod) ->
{
logHandlerMethodDetails(handlerMethod, requestMappingInfo);
});
RequestMappingHandlerMapping
在初始化时会执行以下步骤:
- 组件扫描:扫描所有带有
@Controller
或@RestController
注解的类 - 方法解析:解析类和方法上的
@RequestMapping
及其变体注解 - 条件封装:将 URL 路径、HTTP 方法、请求参数、请求头等条件封装为
RequestMappingInfo
- 映射注册:将
RequestMappingInfo
与对应的HandlerMethod
(封装控制器方法)存入MappingRegistry
2.2 请求匹配的核心逻辑
当请求到达时,RequestMappingHandlerMapping
会执行复杂的匹配逻辑,找到最适合的处理器方法:
- 路径匹配:根据请求 URL 匹配
@RequestMapping
定义的路径模式(支持 Ant 风格通配符) - 方法匹配:验证请求的 HTTP 方法(GET/POST/PUT 等)是否与注解定义一致
- 参数匹配:检查请求是否包含
params
属性指定的参数(如params = "id"
要求必须包含 id 参数) - 头信息匹配:验证请求头是否满足
headers
属性的要求(如headers = "X-Auth"
要求包含 X-Auth 头) - 内容类型匹配:根据
consumes
属性验证请求的Content-Type
(如consumes = "application/json"
)
以代码中的getUser
方法为例:
@GetMapping(value = "/user", params = "id", headers = "X-Auth", consumes = "application/json")
public String getUser(@RequestParam("id") Long id, @RequestHeader("X-Auth") String token) {
// 业务逻辑
}
该方法的匹配条件被封装为RequestMappingInfo
,包含:
- 路径:
/dispatcher/servlet/user
(类上的@RequestMapping
与方法上的路径拼接) - HTTP 方法:GET
- 参数条件:必须包含 id 参数
- 请求头条件:必须包含 X-Auth 头
- 内容类型:仅接受 application/json
三、自定义参数解析:HandlerMethodArgumentResolver 的实践
Spring MVC 默认支持多种参数类型的解析(如@RequestParam
、@RequestHeader
等),但在实际开发中,我们常常需要自定义参数解析逻辑,例如统一的 Token 参数处理。
3.1 自定义参数解析器的实现
代码中的TokenArgumentResolver
实现了HandlerMethodArgumentResolver
接口,用于解析带有@Token
注解的参数:
@Slf4j
public class TokenArgumentResolver
implements HandlerMethodArgumentResolver {
private static final String DEFAULT_TOKEN_HEADER = "X-Auth-Token";
private static final String DEFAULT_TOKEN_PARAM = "authToken";
private static final boolean DEFAULT_VALIDATE_TOKEN = true;
@Override
public boolean supportsParameter(@NonNull MethodParameter parameter) {
Token tokenAnnotation = parameter.getParameterAnnotation(Token.class)
;
if (nonNull(tokenAnnotation)) {
log.debug("Detected Token annotation on parameter: {}", parameter.getParameterName());
return true;
}
return false;
}
@Override
public Object resolveArgument(@NonNull MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
@NonNull NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) {
// 多路径获取token
String token = getHeaderToken(webRequest).orElseGet(() ->
getParameterToken(webRequest).orElse(null));
log.info("Resolved token: {}", maskToken(token));
if (DEFAULT_VALIDATE_TOKEN &&
nonNull(token)) {
validateJwtToken();
}
return token;
}
/**
* 从请求头获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getHeaderToken(NativeWebRequest request) {
// 可配置参数
String tokenHeader = DEFAULT_TOKEN_HEADER;
String headerValue = request.getHeader(tokenHeader);
log.debug("Checking header token from: {}", tokenHeader);
return Optional.ofNullable(headerValue);
}
/**
* 从请求参数获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getParameterToken(NativeWebRequest request) {
String tokenParam = DEFAULT_TOKEN_PARAM;
String paramValue = request.getParameter(tokenParam);
log.debug("Checking parameter token from: {}", tokenParam);
return Optional.ofNullable(paramValue);
}
/**
* JWT Token校验
*/
private void validateJwtToken() {
}
private String maskToken(String token) {
return token != null && token.length() >
4
? "****" + token.substring(token.length() - 4)
: "****";
}
}
3.2 参数解析器的注册与执行流程
自定义参数解析器需要注册到RequestMappingHandlerAdapter
中才能生效:
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
log.debug("Creating custom RequestMappingHandlerAdapter");
MyRequestMappingHandlerAdapter adapter = new MyRequestMappingHandlerAdapter();
// 添加自定义参数解析器
TokenArgumentResolver tokenResolver = new TokenArgumentResolver();
adapter.setCustomArgumentResolvers(List.of(tokenResolver));
// 添加自定义返回值处理器
JsonReturnValueHandler jsonHandler = new JsonReturnValueHandler();
adapter.setCustomReturnValueHandlers(List.of(jsonHandler));
log.info("Custom adapter configured with {} argument resolvers and {} return handlers",
Objects.requireNonNull(adapter.getCustomArgumentResolvers()).size(),
Objects.requireNonNull(adapter.getCustomReturnValueHandlers()).size());
return adapter;
}
参数解析的执行流程如下:
- 预处理:DispatcherServlet 接收到请求后,通过 HandlerMapping 找到对应的 HandlerMethod
- 适配准备:DispatcherServlet 调用 HandlerAdapter 的
supports()
方法判断是否支持该 HandlerMethod - 参数解析:HandlerAdapter 遍历所有注册的 ArgumentResolver,通过
supportsParameter()
找到合适的解析器 - 方法调用:使用解析得到的参数值,通过反射调用目标处理器方法
四、自定义返回值处理:HandlerMethodReturnValueHandler 的应用
除了参数解析,Spring MVC 还允许我们自定义返回值的处理逻辑。代码中的JsonReturnValueHandler
实现了HandlerMethodReturnValueHandler
接口,用于将方法返回值自动序列化为 JSON 格式。
4.1 自定义返回值处理器的实现
@Slf4j
public class JsonReturnValueHandler
implements HandlerMethodReturnValueHandler {
private static final String DEFAULT_CHARSET = "UTF-8";
private static final String APPLICATION_JSON = MediaType.APPLICATION_JSON_VALUE;
@Override
public boolean supportsReturnType(MethodParameter returnType) {
log.debug("Checking return type support: {}", returnType.getParameterName());
return returnType.hasMethodAnnotation(JsonObject.class)
;
}
@Override
public void handleReturnValue(Object returnValue, @NonNull MethodParameter returnType, @NonNull ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
log.info("Handling JSON return value for method: {}", Objects.requireNonNull(returnType.getMethod()).getName());
// 获取响应对象
HttpServletResponse response = Objects.requireNonNull(
webRequest.getNativeResponse(HttpServletResponse.class)
,
"HttpServletResponse not found in request"
);
// 设置响应头
response.setContentType(APPLICATION_JSON);
response.setCharacterEncoding(DEFAULT_CHARSET);
log.debug("Set response content type to {}", APPLICATION_JSON);
// 序列化处理
String jsonStr = JSON.toJSON(returnValue);
log.debug("Serialized return value: {}", jsonStr);
// 写入响应体
response.getWriter().write(jsonStr);
response.getWriter().flush();
// 标记请求已处理
mavContainer.setRequestHandled(true);
log.debug("Marked request as fully handled");
}
}
4.2 返回值处理的核心机制
返回值处理的关键在于mavContainer.setRequestHandled(true)
这行代码,它告诉 DispatcherServlet:
- 该请求的响应已经完全处理完毕
- 不需要再进行后续的视图解析和视图渲染流程
- 直接返回响应给客户端
这与传统的 MVC 流程(处理器→ModelAndView→视图解析→渲染)不同,适用于 RESTful API 等不需要视图渲染的场景。
五、完整请求处理流程:从请求到响应的全链路
结合以上所有组件,我们可以梳理出 Spring MVC 完整的请求处理流程:
graph TD
A[客户端发送请求] --> B[Tomcat容器接收请求]
B --> C[DispatcherServlet拦截请求]
C --> D[调用HandlerMapping查找HandlerMethod]
D --> E[构建HandlerExecutionChain(包含拦截器)]
E --> F[调用HandlerAdapter适配处理器]
F --> G[HandlerAdapter调用ArgumentResolver解析参数]
G --> H[反射调用目标Controller方法]
H --> I[HandlerAdapter调用ReturnValueHandler处理返回值]
I --> J[通过拦截器的afterCompletion方法]
J --> K[返回响应给客户端]
各阶段的核心职责
- 请求接收阶段:Tomcat 容器将 HTTP 请求封装为 HttpServletRequest 对象
- 请求分发阶段:DispatcherServlet 作为前端控制器,协调各组件工作
- 处理器查找阶段:HandlerMapping 根据请求信息找到匹配的 HandlerMethod
- 参数解析阶段:ArgumentResolver 解析请求参数,为方法调用做准备
- 方法调用阶段:通过反射调用 Controller 的目标方法
- 返回值处理阶段:ReturnValueHandler 处理方法返回值,生成响应
- 响应返回阶段:将处理结果封装为 HTTP 响应,返回给客户端
六、自定义配置的最佳实践与扩展建议
基于本文的代码示例,我们可以总结出 Spring MVC 自定义配置的最佳实践:
6.1 组件扩展的原则
- 面向接口编程:自定义组件应实现 Spring 提供的标准接口(如 HandlerMethodArgumentResolver),而非继承具体实现类
- 优先级控制:通过
setCustomArgumentResolvers
而非addArgumentResolvers
来确保自定义组件的优先级 - 职责单一:每个自定义组件应只负责单一功能(如 Token 解析、JSON 序列化)
- 可配置性:将硬编码的配置(如 Token 的请求头名称)改为可配置项,通过
@ConfigurationProperties
绑定
6.2 常见扩展场景
- 统一认证授权:扩展 TokenArgumentResolver,集成 JWT、OAuth2 等认证机制
- 统一返回格式:自定义 ReturnValueHandler,将所有接口返回值封装为统一格式(如 {code:200, data:{}, message:“success”})
- 特殊参数支持:实现自定义 ArgumentResolver,支持如日期、枚举等特殊类型的参数解析
- 全局异常处理:扩展 HandlerExceptionResolver,统一处理业务异常和系统异常
6.3 性能优化建议
- 组件复用:避免频繁创建自定义组件实例,通过 Spring Bean 管理实现复用
- 缓存机制:对请求映射、参数解析结果等进行缓存,减少重复计算
- 异步处理:对于耗时操作,使用
@Async
注解实现异步处理,提高系统吞吐量 - 拦截器优化:合理使用拦截器,避免在拦截器中执行耗时操作
七、总结
Spring MVC 通过 DispatcherServlet、HandlerMapping、HandlerAdapter 等核心组件,构建了一个松耦合、高可扩展的 MVC 框架。本文通过一份完整的自定义配置代码,深入解析了 Spring MVC 的底层原理,包括:
- DispatcherServlet 的初始化和九大核心组件
- RequestMappingHandlerMapping 的请求映射机制
- HandlerMethodArgumentResolver 的自定义参数解析
- HandlerMethodReturnValueHandler 的自定义返回值处理
- 完整的请求处理流程和组件协作机制
完整代码
package com.dwl.mvc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName DispatcherServletController
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
@Controller
@RequestMapping("/dispatcher/servlet/")
public class DispatcherServletController
{
@GetMapping("index")
public String index() {
log.debug("Handling index request");
return "index";
}
@GetMapping("param")
public String param(@RequestParam("name") String name) {
log.info("Received param request with name: {}", name);
return name;
}
@GetMapping(value = "/user", params = "id", headers = "X-Auth", consumes = "application/json")
public String getUser(@RequestParam("id") Long id, @RequestHeader("X-Auth") String token) {
log.info("Processing user request - ID: {}, Token: {}", id, token);
return id + "+" + token;
}
@PutMapping("/putMapping")
public String putMapping(@Token String token) {
log.debug("PUT request received with token: {}", token);
return token;
}
@RequestMapping("/jsonObject")
@JsonObject
public User jsonObject() {
log.info("Generating JSON response");
User user = new User();
user.setName("Dwl");
user.setAge(18);
return user;
}
@Data
public static class User
{
private String name;
private Integer age;
}
}
package com.dwl.mvc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName Json
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonObject {
}
package com.dwl.mvc;
import com.dwl.json.JSON;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.util.Objects;
/**
* @ClassName JsonReturnValueHandler
* @Description JSON 返回值处理器
* 该处理器通过 {@link JsonObject} 注解标识需要 JSON 序列化的返回值,
* 自动将对象转换为 JSON 格式并设置响应头
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
public class JsonReturnValueHandler
implements HandlerMethodReturnValueHandler {
private static final String DEFAULT_CHARSET = "UTF-8";
private static final String APPLICATION_JSON = MediaType.APPLICATION_JSON_VALUE;
@Override
public boolean supportsReturnType(MethodParameter returnType) {
log.debug("Checking return type support: {}", returnType.getParameterName());
return returnType.hasMethodAnnotation(JsonObject.class)
;
}
@Override
public void handleReturnValue(Object returnValue, @NonNull MethodParameter returnType, @NonNull ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
log.info("Handling JSON return value for method: {}", Objects.requireNonNull(returnType.getMethod()).getName());
// 获取响应对象
HttpServletResponse response = Objects.requireNonNull(
webRequest.getNativeResponse(HttpServletResponse.class)
,
"HttpServletResponse not found in request"
);
// 设置响应头
response.setContentType(APPLICATION_JSON);
response.setCharacterEncoding(DEFAULT_CHARSET);
log.debug("Set response content type to {}", APPLICATION_JSON);
// 序列化处理
String jsonStr = JSON.toJSON(returnValue);
log.debug("Serialized return value: {}", jsonStr);
// 写入响应体
response.getWriter().write(jsonStr);
response.getWriter().flush();
// 标记请求已处理
mavContainer.setRequestHandled(true);
log.debug("Marked request as fully handled");
}
}
package com.dwl.mvc;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.List;
import java.util.Objects;
/**
* @ClassName MvcConfig
* @Description MVC 核心配置类
* <p>
* 功能特性:
* 1. 自动扫描组件(排除 Controller 类,由其他配置处理)
* 2. 配置内嵌 Tomcat 容器参数
* 3. 注册 DispatcherServlet 到 Servlet 容器
* 4. 绑定 MVC 相关配置属性
* <p>
* 设计原理:
* - 基于 Spring Boot 自动配置机制,扩展 WebMvc 配置
* - 通过属性绑定实现配置文件与 Java 对象的映射
* - 遵循 Servlet 规范实现 Servlet 容器初始化
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
* @see DispatcherServlet Spring MVC 的核心调度器
* @see TomcatServletWebServerFactory Tomcat 容器工厂
*/
@Configuration
@ComponentScan
@PropertySource(
value = "classpath:application.yaml",
factory = YamlPropertySourceFactory.class // 自定义 YAML 属性源工厂
// 说明:默认只支持 properties 文件,需通过工厂解析 YAML 格式
)
@EnableConfigurationProperties({
// 启用配置属性绑定
WebMvcProperties.class,
// MVC 框架配置
ServerProperties.class // 服务器配置
})
@Slf4j
public class MvcConfig
{
/**
* 创建内嵌 Tomcat 容器工厂
* <p>
* 配置项来源:
* - application.yaml 中的 server.port
* - server.tomcat.connection-timeout
* 设计要点:
* - 通过参数注入 ServerProperties 实现配置解耦
* - 提供默认端口 8080 的容错机制
*/
@Bean
@Nullable
public TomcatServletWebServerFactory tomcatServletWebServerFactory(@Nullable ServerProperties serverProperties // 通过参数注入配置属性
) {
return new TomcatServletWebServerFactory(serverProperties != null ? serverProperties.getPort() : 8080);
}
/**
* 创建 DispatcherServlet 实例
* <p>
* 核心流程:
* 1. 初始化 Spring MVC 的核心调度器
* 2. 延迟初始化策略:由 Servlet 容器首次访问时初始化
* 3. 支持异步请求处理(Servlet 3.0+)
* 生命周期:
* - 构造阶段:创建 Servlet 实例
* - 初始化阶段:由容器调用 init() 方法
* - 销毁阶段:由容器调用 destroy() 方法
*/
@Bean
public DispatcherServlet dispatcherServlet() {
/*
* 在 onRefresh()方法中,DispatcherServlet 调用 initStrategies()初始化九大核心策略组件
* 组件类型 作用 默认实现
* MultipartResolver 处理文件上传请求 StandardServletMultipartResolver
* HandlerMapping 将请求 URL 映射到具体处理器(Controller) RequestMappingHandlerMapping(注解驱动)
* HandlerAdapter 调用处理器方法,支持不同参数解析和返回值处理 RequestMappingHandlerAdapter
* ViewResolver 将逻辑视图名解析为具体视图(如 JSP、Thymeleaf) InternalResourceViewResolver
* LocaleResolver 解析客户端区域信息,支持国际化 AcceptHeaderLocaleResolver
* ThemeResolver 解析应用主题(如 CSS 样式) FixedThemeResolver
* HandlerExceptionResolver 统一处理请求过程中的异常 ExceptionHandlerExceptionResolver
* RequestToViewNameTranslator 自动生成视图名(若未显式指定) DefaultRequestToViewNameTranslator
* FlashMapManager 管理跨请求数据传递(如重定向时传递参数) SessionFlashMapManager
*/
log.info("Creating DispatcherServlet instance");
return new DispatcherServlet();
}
/**
* 注册 DispatcherServlet 到 Servlet 容器
* <p>
* 关键配置项:
* - URL 映射路径:根路径 "/" 接收所有请求
* - 加载顺序:通过 loadOnStartup 控制初始化优先级
* - Servlet 名称:默认 "dispatcherServlet"
* 扩展点:
* - 可配置多个 DispatcherServlet 实现多应用上下文
* - 支持自定义 Servlet 初始化参数
*/
@Bean
@Nullable
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(
@Nullable DispatcherServlet servlet, // 注入创建的 Servlet 实例
@Nullable WebMvcProperties mvcProperties // 绑定 MVC 配置属性
) {
// 处理可能的null值
int loadOnStartup = mvcProperties != null ? mvcProperties.getServlet().getLoadOnStartup() : -1;
// 没有其他路径,请求都会和'/'匹配,由servlet请求分发到其他的控制器上
DispatcherServletRegistrationBean bean = new DispatcherServletRegistrationBean(servlet != null ? servlet : new DispatcherServlet(), "/");
/*
* setLoadOnStartups设置大于0的值,会在tomcat启动时进行初始化
* 设置初始化顺序(数值越小优先级越高)
* 对应配置文件的配置
* spring:
* mvc:
* servlet:
* load-on-startup: 1
*/
bean.setLoadOnStartup(loadOnStartup);
return bean;
}
/**
* 1.默认行为
* DispatcherServlet在初始化时会从 Spring 容器中查找 HandlerMapping类型的 Bean,并将其作为成员变量。
* 默认实现:如果未显式配置 RequestMappingHandlerMapping,Spring 会通过 DispatcherServlet.properties文件中的默认配置加载 RequestMappingHandlerMapping(前提是容器中存在该 Bean)
* 自动注册:若容器中存在 @Controller或 @RestController注解的类,Spring 会自动扫描并注册 RequestMappingHandlerMapping,无需手动配置
* 2.显式配置的优先级
* 如果用户显式配置了自定义的 HandlerMapping(如 BeanNameUrlHandlerMapping),则 DispatcherServlet会优先使用显式配置的组件
* RequestMappingHandlerMapping是 Spring Boot 通过 WebMvcAutoConfiguration自动注册的 Bean。
* 未启用自动配置:若未使用 @SpringBootApplication或 @EnableAutoConfiguration,Spring 无法自动创建该 Bean
*/
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
/**
* 自定义请求处理适配器
* <p>
* 扩展功能:
* - 添加Token参数解析器
* - 注册JSON返回值处理器
* - 自定义消息转换器
*
* @return 自定义适配器Bean
*/
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
log.debug("Creating custom RequestMappingHandlerAdapter");
MyRequestMappingHandlerAdapter adapter = new MyRequestMappingHandlerAdapter();
// 添加自定义参数解析器
TokenArgumentResolver tokenResolver = new TokenArgumentResolver();
adapter.setCustomArgumentResolvers(List.of(tokenResolver));
// 添加自定义返回值处理器
JsonReturnValueHandler jsonHandler = new JsonReturnValueHandler();
adapter.setCustomReturnValueHandlers(List.of(jsonHandler));
log.info("Custom adapter configured with {} argument resolvers and {} return handlers",
Objects.requireNonNull(adapter.getCustomArgumentResolvers()).size(),
Objects.requireNonNull(adapter.getCustomReturnValueHandlers()).size());
return adapter;
}
}
package com.dwl.mvc;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* @ClassName MyRequestMappingHandlerAdapter
* @Description 自定义请求映射处理器适配器
* <p>
* 扩展功能:
* 1. 增强请求参数校验
* 2. 添加请求处理日志
* 3. 支持自定义参数解析器
* 4. 增强返回值处理
* 5. 异常统一处理
* </p>
* <p>
* 设计原理:
* - 继承RequestMappingHandlerAdapter实现请求处理链扩展
* - 重写invokeHandlerMethod方法添加自定义逻辑
* - 通过参数解析器和返回值处理器实现扩展点
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
public class MyRequestMappingHandlerAdapter
extends RequestMappingHandlerAdapter {
@Override
public ModelAndView invokeHandlerMethod(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull HandlerMethod handlerMethod) throws Exception {
return super.invokeHandlerMethod(request, response, handlerMethod);
}
}
package com.dwl.mvc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName Token
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
}
package com.dwl.mvc;
import io.micrometer.common.lang.Nullable;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.util.Optional;
import static java.util.Objects.nonNull;
/**
* @ClassName TokenArgumentResolver
* @Description Token参数解析器
* <p>
* 功能特性:
* 1. 自动解析@Token注解标记的参数
* 2. 支持请求头/请求参数双路径获取token
* 3. 集成JWT校验逻辑
* 4. 提供默认值配置
* 5. 异常处理机制
* </p>
* <p>
* 设计原理:
* - 实现HandlerMethodArgumentResolver接口
* - 优先从请求头获取token,回退到请求参数
* - 支持JWT格式token的自动解析
* - 集成Spring Security上下文
* </p>
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
public class TokenArgumentResolver
implements HandlerMethodArgumentResolver {
private static final String DEFAULT_TOKEN_HEADER = "X-Auth-Token";
private static final String DEFAULT_TOKEN_PARAM = "authToken";
private static final boolean DEFAULT_VALIDATE_TOKEN = true;
@Override
public boolean supportsParameter(@NonNull MethodParameter parameter) {
Token tokenAnnotation = parameter.getParameterAnnotation(Token.class)
;
if (nonNull(tokenAnnotation)) {
log.debug("Detected Token annotation on parameter: {}", parameter.getParameterName());
return true;
}
return false;
}
@Override
public Object resolveArgument(@NonNull MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
@NonNull NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) {
// 多路径获取token
String token = getHeaderToken(webRequest).orElseGet(() ->
getParameterToken(webRequest).orElse(null));
log.info("Resolved token: {}", maskToken(token));
if (DEFAULT_VALIDATE_TOKEN &&
nonNull(token)) {
validateJwtToken();
}
return token;
}
/**
* 从请求头获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getHeaderToken(NativeWebRequest request) {
// 可配置参数
String tokenHeader = DEFAULT_TOKEN_HEADER;
String headerValue = request.getHeader(tokenHeader);
log.debug("Checking header token from: {}", tokenHeader);
return Optional.ofNullable(headerValue);
}
/**
* 从请求参数获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getParameterToken(NativeWebRequest request) {
String tokenParam = DEFAULT_TOKEN_PARAM;
String paramValue = request.getParameter(tokenParam);
log.debug("Checking parameter token from: {}", tokenParam);
return Optional.ofNullable(paramValue);
}
/**
* JWT Token校验
*/
private void validateJwtToken() {
}
private String maskToken(String token) {
return token != null && token.length() >
4
? "****" + token.substring(token.length() - 4)
: "****";
}
}
package com.dwl.mvc;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.util.Properties;
/**
* @ClassName YamlPropertySourceFactory
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
public class YamlPropertySourceFactory
implements PropertySourceFactory {
@Override
@NonNull
public PropertySource<
?> createPropertySource(@Nullable String name, EncodedResource resource) {
// 1. 创建 YAML 解析器
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
// 2. 解析 YAML 到 Properties 对象
Properties properties = factory.getObject();
// 3. 获取文件名(处理空名情况)
String sourceName = name != null ? name : resource.getResource().getFilename();
// 4. 创建 Spring 可识别的属性源
assert properties != null;
return new PropertiesPropertySource(sourceName != null ? sourceName : "yaml", properties);
}
}
package com.dwl.mvc;
import com.dwl.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @ClassName DispatcherServletInitCase
* @Description DispatcherServlet初始化调试案例
* <p>
* 核心功能:
* 1. 请求映射注册的底层实现跟踪
* 2. 请求处理链的完整执行流程
* 3. 参数解析与返回值处理的调试信息
* 4. 拦截器执行顺序的可视化监控
* </p>
* <p>
* 设计原理:
* - 基于Spring MVC的DispatcherServlet初始化流程
* - 通过RequestMappingHandlerMapping解析请求映射
* - 使用HandlerExecutionChain管理拦截器链
* - 通过HandlerAdapter调用目标处理器方法
* </p>
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
public class DispatcherServletInitCase
{
public static void main(String[] args) throws Exception {
// 初始化Spring Web容器
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(MvcConfig.class)
;
/*
* 1.请求映射管理
* 扫描与注册:自动扫描所有带有 @Controller或 @RestController注解的类,解析类和方法上的 @RequestMapping及其变体注解(如 @GetMapping、@PostMapping),生成 请求映射规则(RequestMappingInfo)。
* 映射表维护:将生成的 RequestMappingInfo与对应的 HandlerMethod(封装了控制器方法的对象)关联,存储在内部的 MappingRegistry中,形成请求到处理方法的映射表。
*
* 2.请求匹配与路由
* 请求解析:当请求到达时,解析请求的 URL、HTTP 方法、请求参数、请求头等信息。
* 匹配逻辑:根据 RequestMappingInfo中的条件(路径模式、HTTP 方法、参数条件等),从映射表中筛选出最匹配的 HandlerMethod。
* 返回执行链:将匹配的 HandlerMethod封装为 HandlerExecutionChain(包含拦截器链),返回给 DispatcherServlet执行
*/
RequestMappingHandlerMapping mapping =
context.getBean(RequestMappingHandlerMapping.class)
;
log.info("========== 请求映射注册详情 ==========");
// 遍历所有已注册的处理器方法
mapping.getHandlerMethods().forEach((requestMappingInfo, handlerMethod) ->
{
logHandlerMethodDetails(handlerMethod, requestMappingInfo);
});
set(context, null, "/dispatcher/servlet/index", "GET");
set(context, Map.of("name", "Dwl"), "/dispatcher/servlet/param", "GET");
set(context, Map.of("token", "Dwl"), "/dispatcher/servlet/putMapping", "PUT");
set(context, null, "/dispatcher/servlet/jsonObject", "POST");
}
/**
* 打印处理器方法详细信息
*
* @param handlerMethod 处理器方法
* @param mappingInfo 请求映射信息
*/
private static void logHandlerMethodDetails(HandlerMethod handlerMethod,
RequestMappingInfo mappingInfo) {
log.info("【处理器方法】{}", handlerMethod.getMethod().toGenericString());
log.info("【所属类】{}", handlerMethod.getBeanType().getSimpleName());
// 解析请求映射条件
log.info("【请求路径】{}", getPathPatterns(mappingInfo));
log.info("【HTTP方法】{}", getHttpMethods(mappingInfo));
log.info("【参数条件】{}", getParamsConditions(mappingInfo));
log.info("【请求头】{}", getHeadersConditions(mappingInfo));
log.info("【内容类型】{}", getConsumesConditions(mappingInfo));
log.info("【响应类型】{}", getProducesConditions(mappingInfo));
log.info("----------------------------------------");
}
/**
* 请求条件解析工具方法
*/
private static String getPathPatterns(RequestMappingInfo info) {
return Objects.requireNonNull(info.getPatternsCondition()).getPatterns().stream()
.map(path -> path + (path.endsWith("/") ? "" : "/"))
.collect(Collectors.joining(" | "));
}
private static String getHttpMethods(RequestMappingInfo info) {
return info.getMethodsCondition().getMethods().stream()
.map(RequestMethod::name)
.collect(Collectors.joining(" | "));
}
private static String getParamsConditions(RequestMappingInfo info) {
return info.getParamsCondition().getExpressions().stream()
.map(Object::toString)
.collect(Collectors.joining(" & "));
}
private static String getHeadersConditions(RequestMappingInfo info) {
return info.getHeadersCondition().getExpressions().stream()
.map(Object::toString)
.collect(Collectors.joining(" & "));
}
private static String getConsumesConditions(RequestMappingInfo info) {
return info.getConsumesCondition().getConsumableMediaTypes().stream()
.map(MediaType::toString)
.collect(Collectors.joining(" | "));
}
private static String getProducesConditions(RequestMappingInfo info) {
return info.getProducesCondition().getProducibleMediaTypes().stream()
.map(MediaType::toString)
.collect(Collectors.joining(" | "));
}
public static void set(AnnotationConfigServletWebServerApplicationContext context,
Map<
String, ?> params,
String requestURI,
String method) throws Exception {
// 创建请求对象
MockHttpServletRequest request = new MockHttpServletRequest(method, requestURI);
if (params != null) {
params.forEach((k, v) ->
{
request.addParameter(k, v.toString());
request.addHeader(k, v.toString());
// 模拟Header参数
});
}
// 获取处理链(包含拦截器和处理器)
HandlerExecutionChain chain =
context.getBean(RequestMappingHandlerMapping.class)
.getHandler(request);
log.info("【处理链信息】拦截器数量: {}", Objects.requireNonNull(Objects.requireNonNull(chain).getInterceptors()).length);
log.info("【目标处理器】{}", chain.getHandler());
// 创建响应对象
MockHttpServletResponse response = new MockHttpServletResponse();
// 获取自定义适配器
MyRequestMappingHandlerAdapter adapter =
context.getBean(MyRequestMappingHandlerAdapter.class)
;
// 执行请求处理流程
log.info("========== 开始处理请求 ==========");
adapter.invokeHandlerMethod(request, response,
ObjectUtil.cast(chain.getHandler()));
// 打印处理链信息
log.info("========== 处理链执行完成 ==========");
log.info("【响应状态】{}", response.getStatus());
log.info("【响应内容】{}", response.getContentAsString());
// 打印组件信息
printComponentDetails(adapter);
byte[] arr = response.getContentAsByteArray();
System.out.println(new String(arr, StandardCharsets.UTF_8));
}
private static void printComponentDetails(MyRequestMappingHandlerAdapter adapter) {
log.info("========== 组件信息 ==========");
Objects.requireNonNull(adapter.getArgumentResolvers()).forEach(resolver ->
{
log.info("参数解析器: {}", resolver.getClass().getSimpleName());
});
Objects.requireNonNull(adapter.getReturnValueHandlers()).forEach(handler ->
{
log.info("返回值处理器: {}", handler.getClass().getSimpleName());
});
}
}