【大白话说Java面试题 第161题】【06_Spring篇】第21题: SpringMVC工作流程

📅 2026/7/9 2:43:29
【大白话说Java面试题 第161题】【06_Spring篇】第21题: SpringMVC工作流程
PDF大白话说Java面试题 — 06_Spring篇第21题SpringMVC 的工作流程DispatcherServlet 的工作流程回答核心考点SpringMVC的工作流程远不止DispatcherServlet → HandlerMapping → HandlerAdapter → ViewResolver的背诵式回答。大厂面试中面试官期望你深入理解DispatcherServlet 的九大组件初始化策略initStrategies、doDispatch()方法的完整源码链路含文件上传预处理、拦截器链执行、异常处理、HandlerMapping 的映射匹配机制URL HTTP Method consumes/produces 多条件匹配、HandlerAdapter 的适配器模式设计参数解析器 返回值处理器、以及前后端分离场景下ResponseBody如何跳过 ViewResolver 直接走HttpMessageConverter。面试官真正想判断的是你是否能从源码级理解 SpringMVC 如何将一个 HTTP 请求转化为 Controller 方法的执行以及各组件如何通过接口抽象实现高度可扩展性。1. 九大核心组件与初始化策略SpringMVC 的DispatcherServlet在初始化时通过initStrategies()方法加载九大核心组件采用约定优于配置原则未显式配置时提供默认实现组件接口/类职责默认实现MultipartResolverMultipartResolver文件上传解析StandardServletMultipartResolverServlet 3.0LocaleResolverLocaleResolver国际化区域解析AcceptHeaderLocaleResolverThemeResolverThemeResolver主题解析FixedThemeResolverHandlerMappingHandlerMapping请求 → 处理器映射RequestMappingHandlerMappingHandlerAdapterHandlerAdapter适配执行处理器RequestMappingHandlerAdapterHandlerExceptionResolverHandlerExceptionResolver异常统一处理ExceptionHandlerExceptionResolverRequestToViewNameTranslatorRequestToViewNameTranslator请求转视图名DefaultRequestToViewNameTranslatorViewResolverViewResolver视图名 → 视图对象InternalResourceViewResolverFlashMapManagerFlashMapManager重定向参数传递SessionFlashMapManager// DispatcherServlet.initStrategies() 源码protectedvoidinitStrategies(ApplicationContextcontext){initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);// 加载所有 HandlerMapping BeaninitHandlerAdapters(context);// 加载所有 HandlerAdapter BeaninitHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}关键设计initHandlerMappings(context)会获取 IoC 容器中所有HandlerMapping类型的 Bean按order属性排序后存入ListHandlerMapping。请求处理时按顺序遍历第一个返回非 null 的HandlerExecutionChain即被采用。这种设计允许自定义HandlerMapping覆盖默认行为。[citation:0]2. doDispatch() 源码级完整流程DispatcherServlet.doDispatch()是请求处理的核心入口采用模板方法模式定义处理骨架protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{HttpServletRequestprocessedRequestrequest;HandlerExecutionChainmappedHandlernull;booleanmultipartRequestParsedfalse;try{ModelAndViewmvnull;ExceptiondispatchExceptionnull;try{// 1. 文件上传预处理将普通 Request 包装为 MultipartHttpServletRequestprocessedRequestcheckMultipart(request);multipartRequestParsed(processedRequest!request);// 2. 获取处理器执行链含 Handler Interceptor 链mappedHandlergetHandler(processedRequest);if(mappedHandlernull){noHandlerFound(processedRequest,response);// 404 处理return;}// 3. 获取处理器适配器适配器模式HandlerAdapterhagetHandlerAdapter(mappedHandler.getHandler());// 4. 执行拦截器 preHandle()任一返回 false 则中断if(!mappedHandler.applyPreHandle(processedRequest,response)){return;}// 5. 实际调用 HandlerController 方法返回 ModelAndViewmvha.handle(processedRequest,response,mappedHandler.getHandler());// 6. 应用默认视图名如果 Handler 返回 null 视图名applyDefaultViewName(processedRequest,mv);// 7. 执行拦截器 postHandle()mappedHandler.applyPostHandle(processedRequest,response,mv);}catch(Exceptionex){dispatchExceptionex;}// 8. 处理视图渲染 / 异常处理 / JSON 序列化processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);}finally{// 9. 清理 Multipart 资源if(multipartRequestParsed){cleanupMultipart(processedRequest);}}}关键设计亮点设计模式体现位置作用模板方法模式doDispatch()定义骨架子类可覆盖getHandler()等 protected 方法统一流程允许定制责任链模式HandlerExecutionChain组合多个HandlerInterceptor可配置拦截器链任意拦截器返回 false 即中断适配器模式HandlerAdapter.supports()HandlerAdapter.handle()统一适配多种处理器类型注解方法、接口实现、Servlet 等策略模式HandlerMapping、ViewResolver等多组件接口不同实现可插拔替换[citation:1]3. HandlerMapping 的映射匹配机制RequestMappingHandlerMapping是 Spring 5 的默认实现在应用启动时扫描所有Controller/RestControllerBean解析RequestMapping注解构建映射元数据// RequestMappingHandlerMapping 核心映射逻辑protectedHandlerMethodgetHandlerInternal(HttpServletRequestrequest)throwsException{StringlookupPathinitLookupPath(request);// 获取请求路径HandlerMethodhandlerMethodlookupHandlerMethod(lookupPath,request);// 查找匹配return(handlerMethod!null?handlerMethod.createWithResolvedBean():null);}映射匹配条件多维度精确匹配匹配维度注解属性示例请求路径value/pathGetMapping(/users/{id})HTTP 方法methodRequestMapping(method RequestMethod.POST)请求参数paramsGetMapping(params version2)请求头headersGetMapping(headers X-Api-Version2)消费内容类型consumesPostMapping(consumes application/json)生产内容类型producesGetMapping(produces application/json)匹配优先级精确路径 带通配符路径 正则路径同时满足的条件越多优先级越高。若多个 HandlerMethod 优先级相同会抛出IllegalStateException ambiguous mapping。[citation:2]4. HandlerAdapter 的适配器模式设计DispatcherServlet通过getHandlerAdapter()遍历所有HandlerAdapter调用supports()找到支持当前 Handler 的适配器protectedHandlerAdaptergetHandlerAdapter(Objecthandler)throwsServletException{for(HandlerAdapterha:this.handlerAdapters){if(ha.supports(handler)){// 适配器判断是否支持该 Handler 类型returnha;}}thrownewServletException(No adapter for handler [handler]);}Spring 内置的 HandlerAdapter 实现实现类支持的 Handler 类型典型场景RequestMappingHandlerAdapterRequestMapping注解的 HandlerMethod现代注解驱动开发最常用HttpRequestHandlerAdapterHttpRequestHandler接口实现静态资源处理、文件下载SimpleControllerHandlerAdapterController接口实现传统 Spring MVC 开发SimpleServletHandlerAdapterServlet接口实现集成遗留 ServletRequestMappingHandlerAdapter 的核心执行链路// 简化的执行流程publicModelAndViewhandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){HandlerMethodhandlerMethod(HandlerMethod)handler;// 1. 参数解析通过 HandlerMethodArgumentResolver 组合解析方法参数Object[]argsresolveArguments(handlerMethod,request,response);// 2. 方法调用反射执行 Controller 方法ObjectreturnValueinvokeHandlerMethod(handlerMethod,args);// 3. 返回值处理通过 HandlerMethodReturnValueHandler 处理返回值returnprocessReturnValue(returnValue,handlerMethod);}[citation:3]5. 前后端分离场景ResponseBody 如何跳过 ViewResolver在现代前后端分离项目中Controller 直接返回 JSON 数据不再需要视图渲染。SpringMVC 通过ResponseBody或RestController实现这一机制// 传统 MVC返回视图名需要 ViewResolverControllerpublicclassUserController{GetMapping(/user/page)publicStringgetUserPage(Modelmodel){// 返回视图名model.addAttribute(user,userService.getById(1L));returnuserDetail;// → ViewResolver → userDetail.html}}// 前后端分离直接返回 JSON跳过 ViewResolverRestController// Controller ResponseBodypublicclassUserApiController{GetMapping(/api/user)publicUsergetUser(){// 返回对象returnuserService.getById(1L);// → RequestResponseBodyMethodProcessor 处理// → HttpMessageConverter如 MappingJackson2HttpMessageConverter// → 序列化为 JSON 直接写回 response}}ResponseBody的处理链路RequestMappingHandlerAdapter调用HandlerMethodReturnValueHandler处理返回值RequestResponseBodyMethodProcessor处理ResponseBody的处理器接管通过HttpMessageConverter消息转换器将 Java 对象序列化为 JSON/XML/Protobuf 等直接写入HttpServletResponse跳过 ViewResolver 和 View 渲染。常用 HttpMessageConverter转换器支持媒体类型功能MappingJackson2HttpMessageConverterapplication/jsonJSON 序列化/反序列化StringHttpMessageConvertertext/plain字符串读写ByteArrayHttpMessageConverterapplication/octet-stream字节数组读写ProtobufHttpMessageConverterapplication/x-protobufProtobuf 序列化[citation:4]6. 拦截器 vs 过滤器执行时机与职责边界对比维度过滤器Filter拦截器HandlerInterceptor所属规范Servlet 规范SpringMVC 规范执行时机DispatcherServlet之前Servlet 容器层面DispatcherServlet之后Handler 执行前后SpringMVC 层面依赖容器依赖 Servlet 容器Tomcat 等依赖 Spring IoC 容器访问范围可拦截所有请求包括静态资源只能拦截被HandlerMapping映射的请求执行顺序按web.xml/Order顺序按HandlerExecutionChain中的顺序典型用途字符编码、登录校验、权限过滤日志记录、性能监控、事务边界执行顺序图解HTTP Request ↓ Filter1.doFilter() → Filter2.doFilter() 【Servlet 层】 ↓ DispatcherServlet.doDispatch() ↓ HandlerInterceptor.preHandle() 【SpringMVC 层 - 前置】 ↓ Handler (Controller) 执行业务逻辑 ↓ HandlerInterceptor.postHandle() 【SpringMVC 层 - 后置】 ↓ View 渲染 / JSON 序列化 ↓ HandlerInterceptor.afterCompletion() 【SpringMVC 层 - 最终】 ↓ Filter2.doFilter() 返回 → Filter1.doFilter() 返回 ↓ HTTP Response关键区别Filter 在DispatcherServlet之前执行可以拦截未映射到 SpringMVC 的请求如静态资源Interceptor 在DispatcherServlet之后执行可以访问 Spring 的上下文和 Handler 信息但无法拦截未映射的请求。[citation:5]7. 异常处理机制DispatcherServlet在doDispatch()的 try-catch 中捕获异常统一转交processDispatchResult()处理privatevoidprocessDispatchResult(HttpServletRequestrequest,HttpServletResponseresponse,HandlerExecutionChainmappedHandler,ModelAndViewmv,Exceptionexception)throwsException{if(exception!null){// 1. 交给 HandlerExceptionResolver 处理异常Objecthandler(mappedHandler!null?mappedHandler.getHandler():null);mvprocessHandlerException(request,response,handler,exception);}if(mv!null!mv.wasCleared()){render(mv,request,response);// 2. 渲染视图或 JSON 错误响应}// 3. 执行拦截器 afterCompletion无论成功或异常都会执行if(mappedHandler!null){mappedHandler.triggerAfterCompletion(request,response,null);}}异常处理优先级ExceptionHandler方法当前 Controller 内ControllerAdviceExceptionHandler全局HandlerExceptionResolver实现类如SimpleMappingExceptionResolver默认 Servlet 容器错误页面。8. 面试官追问与高分回答模板追问 1“请详细描述 SpringMVC 的工作流程”低分回答“请求先到 DispatcherServlet然后找 HandlerMapping再交给 HandlerAdapter 执行最后 ViewResolver 解析视图。”只背了表面流程没有源码级理解高分回答 “SpringMVC 的核心是DispatcherServlet的模板方法模式。完整流程分为 9 步 1.请求接收DispatcherServlet接收 HTTP 请求先通过checkMultipart()处理文件上传 2.查找处理器调用getHandler()遍历所有HandlerMappingRequestMappingHandlerMapping根据 URL HTTP Method consumes/produces 等多条件匹配返回HandlerExecutionChain含 Handler Interceptor 链 3.获取适配器getHandlerAdapter()遍历所有HandlerAdapter通过supports()找到支持当前 Handler 的适配器 4.拦截器前置执行applyPreHandle()所有拦截器的preHandle()顺序执行任一返回 false 则中断 5.执行业务HandlerAdapter.handle()通过反射调用 Controller 方法内部通过HandlerMethodArgumentResolver解析参数HandlerMethodReturnValueHandler处理返回值 6.拦截器后置执行applyPostHandle()可以对ModelAndView做修改 7.视图解析/JSON 序列化processDispatchResult()处理结果。如果是ResponseBodyRequestResponseBodyMethodProcessor通过HttpMessageConverter直接序列化为 JSON 写回响应跳过 ViewResolver如果是传统 MVC则通过ViewResolver解析视图名 8.视图渲染View.render()将 Model 数据填充到模板 9.最终回调triggerAfterCompletion()执行拦截器afterCompletion()无论成功或异常都会执行用于资源清理。 前后端分离项目中步骤 7 的视图解析和步骤 8 的视图渲染会被跳过直接走 JSON 序列化。”追问 2“HandlerMapping 和 HandlerAdapter 为什么要分开设计”高分回答 “这是经典的适配器模式应用目的是解耦查找处理器和执行处理器 -HandlerMapping负责‘找谁处理’根据请求 URL、Method、参数等条件找到对应的 Handler可能是RequestMapping方法、Controller接口实现、HttpRequestHandler等。 -HandlerAdapter负责‘怎么执行’不同类型的 Handler 执行方式不同------注解方法需要反射调用接口实现需要直接调用handleRequest()Servlet 需要调用service()。HandlerAdapter通过supports()判断是否支持某 Handler通过handle()统一执行。 这种设计的优势是高度可扩展新增一种处理器类型如 WebFlux 的 RouterFunction只需新增对应的HandlerMapping和HandlerAdapter无需修改DispatcherServlet的核心逻辑完全符合开闭原则。”追问 3“前后端分离项目中SpringMVC 的 ViewResolver 还有用吗”高分回答 “在纯 REST API 项目中ViewResolver和View渲染步骤确实被跳过但 SpringMVC 的核心流程并未改变。关键区别在于返回值处理 - 传统 MVCController 返回String视图名或ModelAndViewViewResolver解析为View对象再渲染为 HTML。 - 前后端分离Controller 返回对象如UserResponseBody或RestController让RequestResponseBodyMethodProcessor接管通过HttpMessageConverter如MappingJackson2HttpMessageConverter将对象序列化为 JSON直接写入HttpServletResponse。 processDispatchResult()中判断mv ! null时如果 Handler 返回的是ResponseBody类型mv实际为 null或已被标记为已处理因此不会进入render()方法。 但ViewResolver在需要返回错误页面如 404、500 的 HTML 错误页或混合模式部分接口返回 JSON、部分返回页面时仍然有用。”追问 4“SpringMVC 的拦截器和过滤器有什么区别执行顺序是怎样的”高分回答 “两者在规范归属、执行时机、访问范围三个维度有本质区别 -过滤器Filter属于 Servlet 规范在DispatcherServlet之前执行由 Servlet 容器Tomcat管理可以拦截所有请求包括静态资源。典型用途字符编码设置CharacterEncodingFilter、登录校验、跨域处理。 -拦截器Interceptor属于 SpringMVC 规范在DispatcherServlet之后执行由 Spring IoC 容器管理只能拦截被HandlerMapping映射的请求即 Controller 请求。典型用途日志记录、性能监控、权限校验可访问 Spring 上下文。 执行顺序Filter1 → Filter2 → DispatcherServlet → Interceptor.preHandle() → Controller → Interceptor.postHandle() → View 渲染 → Interceptor.afterCompletion() → Filter2 返回 → Filter1 返回。 拦截器的afterCompletion()无论请求成功还是异常都会执行是资源清理和日志记录的最佳位置。”追问 5“DispatcherServlet 是线程安全的吗为什么”高分回答 “是线程安全的。DispatcherServlet在 Spring IoC 容器中默认是单例的但它是无状态的------不会在成员变量中保存请求相关的数据。每个请求的处理数据如HttpServletRequest、HttpServletResponse、ModelAndView都通过方法参数传递不共享状态。 线程安全的关键在于 1.doDispatch()方法内的所有变量processedRequest、mappedHandler、mv等都是局部变量存储在每个线程的栈中线程间隔离 2. 核心组件HandlerMapping、HandlerAdapter等虽然作为成员变量存在但它们是无状态的只负责调度逻辑不保存请求状态 3. 真正保存请求状态的是ThreadLocal如RequestContextHolder每个线程独立。 这种设计与 Servlet 规范一致Servlet 容器Tomcat为每个请求分配一个线程Servlet 实例本身不绑定到特定线程。”追问 6“SpringMVC 如何处理异常ControllerAdvice 是怎么生效的”高分回答 “SpringMVC 的异常处理在processDispatchResult()中统一完成 1.doDispatch()的 try-catch 捕获业务执行中的异常存入dispatchException 2.processDispatchResult()判断exception ! null时调用processHandlerException() 3.processHandlerException()遍历所有HandlerExceptionResolver按优先级尝试处理 - 首先检查当前 Controller 类内的ExceptionHandler方法 - 然后检查ControllerAdvice类中的ExceptionHandler方法 - 最后检查配置的SimpleMappingExceptionResolver等。 ControllerAdvice的生效机制应用启动时ExceptionHandlerExceptionResolver扫描所有ControllerAdviceBean缓存其中的ExceptionHandler方法。处理异常时根据异常类型匹配最合适的ExceptionHandler方法通过反射调用。 如果所有HandlerExceptionResolver都无法处理异常会抛给 Servlet 容器返回默认错误页面。”9. 方案选型速查表业务场景推荐方案核心理由传统服务端渲染JSP/Thymeleaf完整 9 步流程需要 ViewResolver 解析视图并渲染前后端分离 REST APIRestControllerHttpMessageConverter跳过视图渲染直接 JSON 序列化文件上传接口MultipartResolverRequestParam(MultipartFile)自动包装MultipartHttpServletRequest全局异常处理ControllerAdviceExceptionHandler统一异常响应避免每个 Controller 重复处理接口鉴权/日志HandlerInterceptor可访问 Spring 上下文按 URL 精确拦截跨域/编码/全局过滤Filter如CharacterEncodingFilter在 DispatcherServlet 之前执行覆盖所有请求自定义参数解析实现HandlerMethodArgumentResolver如解析自定义注解CurrentUser自定义返回值包装实现HandlerMethodReturnValueHandler如统一包装为ResultT响应体面试官想要的满分总结SpringMVC 的核心设计哲学是“统一入口、组件解耦、高度可扩展”。DispatcherServlet作为前端控制器不直接处理请求而是通过模板方法模式定义doDispatch()的处理骨架将具体工作委派给各个组件HandlerMapping 负责找谁处理RequestMappingHandlerMapping在启动时扫描注解构建映射表运行时根据 URL HTTP Method consumes/produces 等多条件精确匹配返回HandlerExecutionChain含拦截器链。HandlerAdapter 负责怎么执行通过适配器模式统一适配RequestMapping方法、Controller接口、HttpRequestHandler等多种处理器类型内部通过HandlerMethodArgumentResolver解析参数、HandlerMethodReturnValueHandler处理返回值。ViewResolver 负责怎么展示传统 MVC 中将逻辑视图名解析为具体View对象前后端分离项目中ResponseBody让RequestResponseBodyMethodProcessor通过HttpMessageConverter直接序列化为 JSON跳过视图渲染。理解 SpringMVC 必须抓住三个关键点九大组件初始化initStrategies()加载所有组件未配置则提供默认实现“约定优于配置”拦截器执行链preHandle()→ Controller →postHandle()→ 视图渲染 →afterCompletion()任一preHandle()返回 false 即中断但afterCompletion()始终执行前后端分离的链路变化ResponseBody不改变核心流程只是在processDispatchResult()中通过HttpMessageConverter直接写回响应跳过了ViewResolver和View.render()。最后记住Filter 在 DispatcherServlet 之前执行Servlet 层Interceptor 在之后执行SpringMVC 层两者职责边界清晰不可互相替代。觉得对您有帮助麻烦点点关注啦您的关注是我创作的最大动力~