深入 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;@Overridepublic 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;}@Overridepublic Object resolveArgument(@NonNull MethodParameter parameter,@Nullable ModelAndViewContainer mavContainer,@NonNull NativeWebRequest webRequest,@Nullable WebDataBinderFactory binderFactory) {// 多路径获取tokenString 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
中才能生效:
@Beanpublic 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;@Overridepublic boolean supportsReturnType(MethodParameter returnType) {log.debug("Checking return type support: {}", returnType.getParameterName());return returnType.hasMethodAnnotation(JsonObject.class);}@Overridepublic 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 TDA[客户端发送请求] --> 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")@JsonObjectpublic User jsonObject() {log.info("Generating JSON response");User user = new User();user.setName("Dwl");user.setAge(18);return user;}@Datapublic 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;@Overridepublic boolean supportsReturnType(MethodParameter returnType) {log.debug("Checking return type support: {}", returnType.getParameterName());return returnType.hasMethodAnnotation(JsonObject.class);}@Overridepublic 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@Nullablepublic 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() 方法*/@Beanpublic 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@Nullablepublic 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*/@Beanpublic RequestMappingHandlerMapping requestMappingHandlerMapping() {return new RequestMappingHandlerMapping();}/*** 自定义请求处理适配器* <p>* 扩展功能:* - 添加Token参数解析器* - 注册JSON返回值处理器* - 自定义消息转换器** @return 自定义适配器Bean*/@Beanpublic 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 {@Overridepublic 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;@Overridepublic 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;}@Overridepublic Object resolveArgument(@NonNull MethodParameter parameter,@Nullable ModelAndViewContainer mavContainer,@NonNull NativeWebRequest webRequest,@Nullable WebDataBinderFactory binderFactory) {// 多路径获取tokenString 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@NonNullpublic 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());});}}