一个请求进来到底发生了什么?Spring MVC 请求处理全链路源码拆解

一个请求进来到底发生了什么?Spring MVC 请求处理全链路源码拆解 一个请求进来到底发生了什么Spring MVC 请求处理全链路源码拆解从浏览器点下按钮到 Controller 方法执行中间经历了多少环节为什么 RequestBody 能自动把 JSON 变成对象为什么返回值自动变成 JSON为什么 404 长那样这一篇把 DispatcherServlet 从请求进入到响应返回的完整链路拆给你看。一、Spring MVC 的核心DispatcherServlet所有请求的总入口是DispatcherServlet。它就是一个特殊的 Servlet拦截所有匹配的 URL然后统一调度。用户请求 │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ DispatcherServlet │ │ │ │ 1. HandlerMapping ──► 找到处理该请求的 Controller 方法 │ │ 2. HandlerAdapter ──► 适配执行 Controller 方法 │ │ 3. 参数解析器 ──► 把请求参数变成方法入参 │ │ 4. Controller 方法 ──► 执行业务逻辑 │ │ 5. 返回值处理器 ──► 把返回值变成响应数据 │ │ 6. 消息转换器 ──► 把数据序列化成 JSON/XML │ │ 7. HandlerException ──► 异常统一处理 │ │ 8. ViewResolver ──► 返回视图时解析视图 │ └──────────────────────────────────────────────────────────────┘ │ ▼ 用户响应这张图里的 8 个环节就是 Spring MVC 的全部家当。下面逐个拆。二、DispatcherServlet 的初始化9 大组件DispatcherServlet在启动时通过initStrategies()初始化 9 大组件组件作用默认实现MultipartResolver文件上传解析StandardServletMultipartResolverLocaleResolver国际化AcceptHeaderLocaleResolverThemeResolver主题FixedThemeResolverHandlerMapping请求→方法映射RequestMappingHandlerMappingHandlerAdapter方法执行适配RequestMappingHandlerAdapterHandlerExceptionResolver异常处理ExceptionHandlerExceptionResolverRequestToViewNameTranslator视图名推断DefaultRequestToViewNameTranslatorViewResolver视图解析ContentNegotiatingViewResolverFlashMapManager重定向传参SessionFlashMapManagerprotected void initStrategies(ApplicationContext context) { initMultipartResolver(context); // 1. 文件上传 initLocaleResolver(context); // 2. 国际化 initThemeResolver(context); // 3. 主题 initHandlerMappings(context); // 4. 映射器 initHandlerAdapters(context); // 5. 适配器 initHandlerExceptionResolvers(context); // 6. 异常 initRequestToViewNameTranslator(context); // 7. 视图名 initViewResolvers(context); // 8. 视图 initFlashMapManager(context); // 9. Flash }启动时找不到自定义实现就用默认的找到了就替换成你的。三、请求处理主链路doDispatch()所有请求都汇聚到DispatcherServlet.doDispatch()。这是整个 Spring MVC 的心脏protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{HttpServletRequestprocessedRequestrequest;HandlerExecutionChainmappedHandlernull;booleanmultipartRequestParsedfalse;try{// 1. 文件上传请求先转成 MultipartHttpServletRequestprocessedRequestcheckMultipart(request);mappedHandlergetHandler(processedRequest);// 2. 找 Handler含拦截器链if(mappedHandlernull){noHandlerFound(processedRequest,response);// 404return;}// 3. 拿到适配器HandlerAdapterhagetHandlerAdapter(mappedHandler.getHandler());// 4. 执行拦截器 preHandleif(!mappedHandler.applyPreHandle(processedRequest,response)){return;}// 5. 真正执行 Controller 方法mvha.handle(processedRequest,response,mappedHandler.getHandler());// 6. 执行拦截器 postHandlemappedHandler.applyPostHandle(processedRequest,response,mv);}catch(Exceptionex){dispatchExceptionex;// 统一异常出口}// 7. 渲染视图 / 写响应体 拦截器 afterCompletionprocessDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);}核心就一句话找 Handler映射→ 适配执行 → 处理结果。下面拆每一步。四、HandlerMapping怎么找到那个方法RequestMappingHandlerMapping在启动时扫描所有Controller把每个RequestMapping方法解析成一个映射RequestMappingInfo存进注册表RequestMapping(/order/{id}) GET │ ▼ RequestMappingInfo{ patterns {/order/{id}}, methods {GET}, params {}, headers {}, consumes {}, produces {} } │ ▼ 映射注册表: MapRequestMappingInfo, HandlerMethod请求进来时通过getHandlerInternal()匹配按 URL 模板匹配支持{id}路径变量再按请求方法GET/POST过滤支持正则、通配符、最佳匹配排序匹配到以后返回一个HandlerExecutionChain——包含HandlerMethod 拦截器链。// 路径变量如何提取HandlerMethodhandlerMethod...;UrlPathHelperhelpernewUrlPathHelper();MapString,StringuriVariableshelper.decodeMatrixVariables(...);// 最后通过 MethodArgumentResolver 注入到 PathVariable 参数五、HandlerAdapter参数是怎么变成方法入参的RequestMappingHandlerAdapter拿着 34 种HandlerMethodArgumentResolver挨个判断这个参数该由谁来解析参数类型解析器解析逻辑PathVariablePathVariableMethodArgumentResolver从 URI 模板变量取RequestParamRequestParamMethodArgumentResolver从 queryString / form 取RequestBodyRequestResponseBodyMethodProcessor读 body → 消息转换器反序列化RequestHeaderRequestHeaderMethodArgumentResolver从请求头取ModelAttributeModelAttributeMethodProcessor绑定表单到对象CookieValueServletCookieValueMethodArgumentResolver从 Cookie 取HttpServletRequestServletRequestMethodArgumentResolver直接注入原始对象RequestPartRequestPartMethodArgumentResolver文件/多部分参数RequestBody的完整流程// 1. 从 body 读出字节流byte[]bodyreadBody(request);// 2. 根据 Content-Type 找消息转换器HttpMessageConverter?convertergetMessageConverter(request);// 3. 从字节流反序列化为对象Objectargconverter.read(type,request);Spring Boot 默认注册了MappingJackson2HttpMessageConverterJSON所以RequestBody OrderDTO能直接把 JSON 变成对象。这就是你啥都没配置就能用的原因。六、返回值处理器返回值怎么变成响应执行完 Controller 方法后HandlerMethodReturnValueHandler决定怎么处理返回值返回值处理器行为ResponseBody对象RequestResponseBodyMethodProcessor消息转换器序列化为 JSONString视图名ViewNameMethodReturnValueHandler找视图渲染ModelAndViewModelAndViewMethodReturnValueHandler直接渲染ResponseEntityHttpEntityMethodProcessor带状态码输出void无响应由 response 对象直接写// ResponseBody 的核心逻辑publicvoidhandleReturnValue(ObjectreturnValue,...){// 1. 找到能写这个类型的消息转换器HttpMessageConverter?converterselectConverter(returnValue);// 2. 把对象序列化后写进 response bodyconverter.write(returnValue,contentType,response);}因为RestControllerController ResponseBody所以每个方法返回值都会走这条 JSON 输出通道。七、消息转换器序列化的最后一公里HttpMessageConverter接口的两个核心方法publicinterfaceHttpMessageConverterT{// 能不能读这个类型booleancanRead(Class?clazz,MediaTypemediaType);// 能不能写这个类型booleancanWrite(Class?clazz,MediaTypemediaType);// 反序列化字节流 → 对象Tread(Class?extendsTclazz,HttpInputMessageinputMessage);// 序列化对象 → 字节流voidwrite(Tt,MediaTypecontentType,HttpOutputMessageoutputMessage);}默认注册的一批转换器按顺序匹配转换器处理内容ByteArrayHttpMessageConverter字节数组StringHttpMessageConverter文本FormHttpMessageConverter表单MappingJackson2HttpMessageConverterJSONMappingJackson2XmlHttpMessageConverterXMLResourceHttpMessageConverter资源下载顺序很重要——Spring 会按getSupportedMediaTypes()匹配Accept头是 JSON 时选 Jackson是 XML 时选 XML 转换器。八、异常处理ExceptionHandlerExceptionResolverController 抛异常后DispatcherServlet兜底调用HandlerExceptionResolverController 抛异常 │ ▼ HandlerExceptionResolver 链 ├─ ExceptionHandlerExceptionResolverExceptionHandler/RestControllerAdvice ├─ ResponseStatusExceptionResolverResponseStatus └─ DefaultHandlerExceptionResolver内置状态码RestControllerAdvice的全局异常处理器就是第一个 resolver 在工作RestControllerAdvicepublicclassGlobalExceptionHandler{ExceptionHandler(BusinessException.class)publicResponseEntityErrorResulthandleBiz(BusinessExceptione){returnResponseEntity.badRequest().body(newErrorResult(e.getCode(),e.getMessage()));}}匹配逻辑ExceptionHandlerExceptionResolver收集所有ExceptionHandler方法按异常类型最接近优先子类 父类。九、一次完整的请求从进入到响应把上面的环节拼起来一个POST /order的完整旅程① 请求进入容器Tomcat ② 路由到 DispatcherServlet.doDispatch() ③ checkMultipart不是文件上传跳过 ④ getHandler匹配 RequestMapping(/order) POST └─ HandlerExecutionChain HandlerMethod 拦截器链 ⑤ 拦截器 preHandle → 认证/日志/限流 ⑥ RequestMappingHandlerAdapter 解析参数 └─ RequestBody OrderDTO └─ MappingJackson2HttpMessageConverter.read() ⑦ 执行 OrderController.create() └─ 业务逻辑返回 OrderVO ⑧ ResponseBody 处理器 └─ MappingJackson2HttpMessageConverter.write(JSON) ⑨ 拦截器 postHandle → afterCompletion ⑩ 响应返回浏览器看到没你在 Controller 里写的那几行业务代码只是整个链路里的一小段。剩下的全部由框架代劳。十、几个高频面试追问10.1 为什么 Transactional 和 Async 经常失效在 MVC 里为什么会调用自身方法失效同一个类内部方法调用不走代理Transactional/Async 都是 AOP 代理拦截的。所以// 错误内部调用不走代理publicvoidouter(){this.inner();// Transactional 失效}// 正确注入自身代理publicvoidouter(){self.inner();// 通过代理调用事务生效}10.2 为什么 URL 匹配/order/{id}能同时匹配/order/1和/order/abc路径变量是字符串匹配{id}不限制类型。1和abc都是合法字符串都匹配。类型转换发生在参数解析器PathVariable里转换失败会抛MethodArgumentTypeMismatchException400。10.3 拦截器Interceptor和过滤器Filter有什么区别FilterInterceptor规范Servlet 规范Spring 框架时机进入 DispatcherServlet 之前DispatcherServlet 之后能拿到request/responseHandlerMethod能改目标方法调用链多个 Filter 链式preHandle → postHandle → afterCompletion10.4 为什么返回对象是 JSON 而不是视图RestController的ResponseBody让返回值处理器选择了 JSON 输出通道。而Controller返回 String 会被当成视图名走 ViewResolver 渲染。十一、总结Spring MVC 的请求处理本质是一个流水线DispatcherServlet 做调度HandlerMapping 做路由HandlerAdapter 做参数与执行消息转换器做序列化异常解析器兜底。你只需要关注我的业务方法怎么写剩下的框架全帮你编排好了。理解了这条链路遇到 404、400、JSON 乱码、参数解析失败这类问题你就能一眼定位到是哪个环节出了问题。