DispatcherServlet请求处理链:从请求到响应的完整源码路径
DispatcherServlet请求处理链:从请求到响应的完整源码路径
适读人群:Spring MVC使用者,希望理解请求处理底层机制的后端开发者 | 阅读时长:约20分钟
开篇故事
三年前我刚加入现在这家公司,接手了一个老系统。有一天测试反馈说某个接口返回了406错误,Not Acceptable。我去翻代码,Controller写得好好的,@GetMapping也对,返回值是个Java对象,加了@ResponseBody。
我折腾了半天,最后发现是一个全局的HandlerInterceptor在postHandle里动了response的ContentType,把它改成了text/plain,导致HttpMessageConverter选择失败。
当时我就想,如果我知道请求在DispatcherServlet里是怎么流转的,这种问题一眼就能看出来。后来我花时间把DispatcherServlet.doDispatch()从头到尾看了一遍,才意识到它的设计有多精妙——请求处理的每个环节都是插件化的,每个组件都可以替换。
今天我把这条完整路径梳理出来。你看完之后,以后碰到任何跟请求处理相关的问题,都能快速找到切入点。
一、DispatcherServlet在Spring MVC中的地位
DispatcherServlet是Spring MVC的核心,所有请求的入口。它继承自HttpServlet,通过标准Servlet规范接收请求,然后按照一套精心设计的策略链处理请求。
核心类:org.springframework.web.servlet.DispatcherServlet,Spring Framework 6.x中位于spring-webmvc模块。
整个处理流程的入口方法是doService()(第883行),然后委托给doDispatch()(第925行),这个方法是真正的核心。
二、源码核心路径解析
2.1 完整请求处理时序图
2.2 doDispatch源码逐行解析
// DispatcherServlet.java 第925行
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 异步处理相关
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 1. 检查是否是multipart请求(文件上传)
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2. 查找Handler(关键步骤)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 没找到Handler → 404
noHandlerFound(processedRequest, response);
return;
}
// 3. 查找HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4. 处理Last-Modified缓存(304优化)
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response)
.checkNotModified(lastModified) && isGet) {
return;
}
}
// 5. 执行拦截器preHandle(任一返回false则中断)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 6. 真正执行Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 7. 处理异步结果
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 8. 设置默认View名(如果没有设置)
applyDefaultViewName(processedRequest, mv);
// 9. 执行拦截器postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new ServletException("...", err);
}
// 10. 处理结果(渲染视图或处理异常)
processDispatchResult(processedRequest, response, mappedHandler,
mv, dispatchException);
}
// ... finally块
}2.3 getHandler:HandlerMapping链
getHandler()遍历所有注册的HandlerMapping,第一个能匹配的返回:
// DispatcherServlet.java 第1182行
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}默认的HandlerMapping优先级顺序(@Order值):
RequestMappingHandlerMapping(处理@RequestMapping注解,Order=0)BeanNameUrlHandlerMapping(按Bean名字匹配URL,Order=2)RouterFunctionMapping(WebFlux用,Servlet环境也会注册,Order=-1)
2.4 HandlerAdapter:适配不同类型的Handler
找到Handler之后,需要找能执行它的HandlerAdapter:
// DispatcherServlet.java 第1218行
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]...");
}常见HandlerAdapter:
RequestMappingHandlerAdapter:处理HandlerMethod(@Controller方法),这是最常用的HttpRequestHandlerAdapter:处理HttpRequestHandler(如静态资源)SimpleControllerHandlerAdapter:处理老式的Controller接口
2.5 RequestMappingHandlerAdapter的invokeHandlerMethod
这里是参数解析、方法调用、返回值处理的核心:
// RequestMappingHandlerAdapter.java 第795行(简化)
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 数据绑定初始化(处理@InitBinder)
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 模型工厂(处理@ModelAttribute)
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 创建可调用的HandlerMethod包装
ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);
// 设置参数解析器(HandlerMethodArgumentResolver链)
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 设置返回值处理器(HandlerMethodReturnValueHandler链)
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// ... 设置binderFactory, modelFactory等
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 执行@ModelAttribute方法
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 调用Controller方法(参数解析 + 反射调用 + 返回值处理)
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// 处理异步
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}2.6 拦截器的执行细节
注意:postHandle和afterCompletion的执行顺序是逆序的,这与preHandle的正序不同。
三、完整代码示例
3.1 自定义HandlerInterceptor实现请求追踪
@Component
public class TraceInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(TraceInterceptor.class);
private static final String TRACE_ID_KEY = "traceId";
private static final String START_TIME_KEY = "startTime";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
if (!(handler instanceof HandlerMethod handlerMethod)) {
return true; // 非@Controller方法,直接放行
}
// 生成或传播TraceId
String traceId = request.getHeader("X-Trace-Id");
if (traceId == null || traceId.isBlank()) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
request.setAttribute(TRACE_ID_KEY, traceId);
request.setAttribute(START_TIME_KEY, System.currentTimeMillis());
// 设置到MDC,让日志自动携带traceId
MDC.put("traceId", traceId);
response.setHeader("X-Trace-Id", traceId);
log.info("[Trace] {} {} -> {}.{}",
request.getMethod(), request.getRequestURI(),
handlerMethod.getBeanType().getSimpleName(),
handlerMethod.getMethod().getName());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
long startTime = (Long) request.getAttribute(START_TIME_KEY);
long cost = System.currentTimeMillis() - startTime;
String traceId = (String) request.getAttribute(TRACE_ID_KEY);
log.info("[Trace] {} {} cost={}ms status={} traceId={}",
request.getMethod(), request.getRequestURI(),
cost, response.getStatus(), traceId);
// 清理MDC,防止线程池复用时traceId污染
MDC.clear();
}
}
// 注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private TraceInterceptor traceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/health", "/api/metrics");
}
}3.2 自定义HandlerMapping实现版本路由
// 自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
public @interface VersionedController {
String version();
}
// 自定义HandlerMapping
@Component
@Order(-1) // 比RequestMappingHandlerMapping优先级高(Order=0)
public class VersionedHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, VersionedController.class);
}
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
if (info == null) return null;
VersionedController annotation =
AnnotationUtils.findAnnotation(handlerType, VersionedController.class);
if (annotation != null) {
// 在路径前加版本前缀
String versionPrefix = "/v" + annotation.version();
RequestMappingInfo prefix = RequestMappingInfo
.paths(versionPrefix)
.options(getBuilderConfiguration())
.build();
return prefix.combine(info);
}
return info;
}
}
// 使用示例
@VersionedController(version = "2")
@RequestMapping("/users")
public class UserControllerV2 {
@GetMapping("/{id}")
@ResponseBody
public UserDTO getUser(@PathVariable Long id) {
// 请求路径变为 /v2/users/{id}
return userService.findById(id);
}
}四、踩坑实录
坑1:postHandle中修改response导致406
开头故事就是这个。原因在于postHandle执行时,响应头已经被RequestResponseBodyMethodProcessor写了Content-Type,在postHandle里再改会导致MessageConverter选择时发现协商结果不匹配。
// 错误:在postHandle里改ContentType
@Override
public void postHandle(...) {
response.setContentType("text/plain"); // 危险!已经太晚了
}
// 正确:在preHandle里设置,或者通过@RequestMapping的produces属性控制
@GetMapping(value = "/data", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Data> getData() { ... }坑2:拦截器preHandle返回false后afterCompletion不执行
现象:在preHandle返回false的情况下,期望afterCompletion能清理MDC,但MDC没被清理,下一个请求打印了上一个请求的traceId。
根因:看源码HandlerExecutionChain.applyPreHandle():
// HandlerExecutionChain.java 第110行
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response)
throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
// preHandle返回false,触发已执行拦截器的afterCompletion
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}triggerAfterCompletion只执行interceptorIndex之前(含)的拦截器的afterCompletion,也就是说只有已经成功执行了preHandle的拦截器才会执行afterCompletion。如果你的清理逻辑在拦截器A,但拦截器B在A之前返回了false,则A的afterCompletion不会执行!
解决方案:把清理逻辑放到返回false之前,或者确保清理拦截器排在最前面。
坑3:HandlerMapping顺序导致请求被错误拦截
现象:新加了一个BeanNameUrlHandlerMapping的Bean,导致某些/api/**路径被错误路由。
根因:DispatcherServlet遍历HandlerMapping列表时,第一个匹配的就返回。如果你注册了多个HandlerMapping,顺序很重要。
// 显式指定Order,确保自定义HandlerMapping不影响@RequestMapping
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) // 放到最后
public BeanNameUrlHandlerMapping customHandlerMapping() {
return new BeanNameUrlHandlerMapping();
}坑4:Filter和Interceptor的区别忽视导致异常处理不一致
Filter是Servlet规范,在DispatcherServlet之外;Interceptor是Spring MVC,在DispatcherServlet之内。
请求 → Filter链 → DispatcherServlet → Interceptor链 → HandlerFilter抛出的异常不会被@ExceptionHandler处理,因为它发生在DispatcherServlet之外。很多人在Filter里抛异常然后疑惑为什么全局异常处理没捕获,就是这个原因。
五、总结与延伸
DispatcherServlet的设计是一个典型的责任链 + 策略模式组合:
HandlerMapping:策略模式,多种查找策略HandlerAdapter:适配器模式,支持多种Handler类型HandlerInterceptor:责任链模式,可插拔的处理链HandlerExceptionResolver:策略模式,多种异常处理策略ViewResolver:策略模式,多种视图解析策略
理解了这个架构,碰到任何Spring MVC相关问题,先判断在哪个组件出了问题,再去看对应源码,思路会清晰很多。
下一篇聊HandlerMethodArgumentResolver,看@RequestBody是怎么把JSON字符串变成Java对象的。
