1. DispatcherServlet的江湖地位如果把Spring MVC框架比作一个大型企业那么DispatcherServlet就是这家企业的前台接待处。所有外部请求相当于来访客户都要先经过这里由它负责协调内部各个部门HandlerMapping、HandlerAdapter等组件完成业务处理。这种设计模式在软件架构中被称为前端控制器模式它的核心价值在于统一入口、集中管控。我见过不少新手在刚接触Spring MVC时会把DispatcherServlet和普通Servlet混为一谈。虽然它确实继承自HttpServlet但它的能耐可大得多。举个生活化的例子普通Servlet就像小区门卫只负责简单的进出登记而DispatcherServlet则是五星级酒店的礼宾部不仅能识别客人身份还能调度客房服务、餐饮部、保洁部等全套服务体系。2. 初始化过程的庖丁解牛2.1 启动时的三级跳DispatcherServlet的初始化就像火箭发射需要经历多级推进// 伪代码展示初始化流程 HttpServletBean.init() → FrameworkServlet.initServletBean() → DispatcherServlet.onRefresh()第一级推进发生在HttpServletBean中这里主要处理web.xml中的基础配置参数。我曾经在一个老项目中踩过坑当时在web.xml里配置了contextConfigLocation参数但忘记加init-param标签导致配置没生效。这种问题就需要追溯到这层初始化阶段来排查。第二级推进由FrameworkServlet完成它的核心任务是创建WebApplicationContext容器。这里有个关键知识点Spring MVC容器会以Spring根容器由ContextLoaderListener创建作为父容器形成经典的父子容器结构。这种设计让业务层和Web层的Bean可以各司其职又相互协作。2.2 九大组件的装载策略当执行到DispatcherServlet.onRefresh()方法时真正的重头戏才开始protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); // 文件上传处理器 initLocaleResolver(context); // 国际化支持 initThemeResolver(context); // 主题换肤 initHandlerMappings(context); // 请求映射处理器核心 initHandlerAdapters(context); // 控制器适配器核心 initHandlerExceptionResolvers(context); // 异常处理器 initRequestToViewNameTranslator(context); // 视图名转换器 initViewResolvers(context); // 视图解析器核心 initFlashMapManager(context); // 闪存数据管理器 }这些组件的初始化有个有趣的规律它们都遵循检测所有→检测指定→使用默认的三段式策略。以HandlerMapping为例先扫描容器中所有HandlerMapping类型的Bean当detectAllHandlerMappings为true时如果没找到再尝试获取名为handlerMapping的特定Bean如果还没有最后加载DispatcherServlet.properties中定义的默认策略这种设计提供了极大的灵活性。在电商项目中我们曾自定义过HandlerMapping来实现特殊的URL路由规则只需要关闭detectAll配置就能让系统精准使用我们的定制组件。3. 请求处理的精妙舞步3.1 请求分发的核心算法doDispatch()方法是整个DispatcherServlet最精华的部分它的处理流程就像精心编排的芭蕾protected void doDispatch(HttpServletRequest request, HttpServletResponse response) { // 1. 检查文件上传请求 processedRequest checkMultipart(request); // 2. 寻找处理器执行链 HandlerExecutionChain mappedHandler getHandler(processedRequest); // 3. 获取适配器 HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler()); // 4. 执行拦截器preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) return; // 5. 实际处理请求 ModelAndView mv ha.handle(processedRequest, response, mappedHandler.getHandler()); // 6. 应用默认视图名 applyDefaultViewName(processedRequest, mv); // 7. 执行拦截器postHandle mappedHandler.applyPostHandle(processedRequest, response, mv); // 8. 处理渲染结果 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }这个过程中最易出问题的环节是HandlerMapping与HandlerAdapter的匹配。有次我们团队引入了一个第三方库它自带的Controller使用了特殊注解结果因为缺少对应的HandlerAdapter导致请求一直返回406错误。后来通过调试发现需要在配置中添加mvc:annotation-driven/来注册默认适配器。3.2 视图渲染的魔法时刻视图解析阶段藏着不少实用技巧。比如在JSP项目中可以配置多个ViewResolver形成解析链Configuration public class ViewConfig implements WebMvcConfigurer { Bean public ViewResolver jspViewResolver() { InternalResourceViewResolver resolver new InternalResourceViewResolver(); resolver.setPrefix(/WEB-INF/views/); resolver.setSuffix(.jsp); resolver.setOrder(2); return resolver; } Bean public ViewResolver excelViewResolver() { return new ExcelViewResolver(); // 自定义Excel视图解析器 } }这种配置下当控制器返回report视图名时系统会先尝试用ExcelViewResolver解析失败后再 fallback 到JSP解析器。我们在做报表导出功能时就利用了这个特性同一个URL通过参数控制返回HTML或Excel格式。4. 实战中的避坑指南4.1 拦截器与异步处理的陷阱在处理异步请求时拦截器的执行顺序会发生变化// 常规流程 preHandle → controller → postHandle → afterCompletion // 异步流程 preHandle → controller (开启异步) → afterConcurrentHandlingStarted → (异步线程) preHandle → controller → postHandle → afterCompletion曾经有个支付回调接口因为没注意这个差异导致日志记录不全。后来我们通过在拦截器中判断isAsyncStarted()来区分处理才解决了问题。4.2 文件上传的内存优化默认情况下文件上传会缓存到内存中。当处理大文件时这个机制可能引发OOM!-- 正确的配置方式 -- bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver property namemaxInMemorySize value51200/ !-- 50KB -- property namemaxUploadSize value52428800/ !-- 50MB -- /bean在云存储项目中我们更进一步实现了自定义的MultipartResolver直接将文件流式传输到OSS完全避免了本地磁盘缓存。DispatcherServlet的设计处处体现着Spring框架的匠心通过分层初始化保证扩展性采用策略模式支持灵活替换利用模板方法规范处理流程。理解这些设计思想比单纯记忆执行流程更有价值。当遇到复杂业务场景时这些底层认知能帮助你快速定位问题甚至开发出自己的定制组件。
【Spring】DispatcherServlet核心流程与组件协作全解析
发布时间:2026/7/16 2:22:04
1. DispatcherServlet的江湖地位如果把Spring MVC框架比作一个大型企业那么DispatcherServlet就是这家企业的前台接待处。所有外部请求相当于来访客户都要先经过这里由它负责协调内部各个部门HandlerMapping、HandlerAdapter等组件完成业务处理。这种设计模式在软件架构中被称为前端控制器模式它的核心价值在于统一入口、集中管控。我见过不少新手在刚接触Spring MVC时会把DispatcherServlet和普通Servlet混为一谈。虽然它确实继承自HttpServlet但它的能耐可大得多。举个生活化的例子普通Servlet就像小区门卫只负责简单的进出登记而DispatcherServlet则是五星级酒店的礼宾部不仅能识别客人身份还能调度客房服务、餐饮部、保洁部等全套服务体系。2. 初始化过程的庖丁解牛2.1 启动时的三级跳DispatcherServlet的初始化就像火箭发射需要经历多级推进// 伪代码展示初始化流程 HttpServletBean.init() → FrameworkServlet.initServletBean() → DispatcherServlet.onRefresh()第一级推进发生在HttpServletBean中这里主要处理web.xml中的基础配置参数。我曾经在一个老项目中踩过坑当时在web.xml里配置了contextConfigLocation参数但忘记加init-param标签导致配置没生效。这种问题就需要追溯到这层初始化阶段来排查。第二级推进由FrameworkServlet完成它的核心任务是创建WebApplicationContext容器。这里有个关键知识点Spring MVC容器会以Spring根容器由ContextLoaderListener创建作为父容器形成经典的父子容器结构。这种设计让业务层和Web层的Bean可以各司其职又相互协作。2.2 九大组件的装载策略当执行到DispatcherServlet.onRefresh()方法时真正的重头戏才开始protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); // 文件上传处理器 initLocaleResolver(context); // 国际化支持 initThemeResolver(context); // 主题换肤 initHandlerMappings(context); // 请求映射处理器核心 initHandlerAdapters(context); // 控制器适配器核心 initHandlerExceptionResolvers(context); // 异常处理器 initRequestToViewNameTranslator(context); // 视图名转换器 initViewResolvers(context); // 视图解析器核心 initFlashMapManager(context); // 闪存数据管理器 }这些组件的初始化有个有趣的规律它们都遵循检测所有→检测指定→使用默认的三段式策略。以HandlerMapping为例先扫描容器中所有HandlerMapping类型的Bean当detectAllHandlerMappings为true时如果没找到再尝试获取名为handlerMapping的特定Bean如果还没有最后加载DispatcherServlet.properties中定义的默认策略这种设计提供了极大的灵活性。在电商项目中我们曾自定义过HandlerMapping来实现特殊的URL路由规则只需要关闭detectAll配置就能让系统精准使用我们的定制组件。3. 请求处理的精妙舞步3.1 请求分发的核心算法doDispatch()方法是整个DispatcherServlet最精华的部分它的处理流程就像精心编排的芭蕾protected void doDispatch(HttpServletRequest request, HttpServletResponse response) { // 1. 检查文件上传请求 processedRequest checkMultipart(request); // 2. 寻找处理器执行链 HandlerExecutionChain mappedHandler getHandler(processedRequest); // 3. 获取适配器 HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler()); // 4. 执行拦截器preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) return; // 5. 实际处理请求 ModelAndView mv ha.handle(processedRequest, response, mappedHandler.getHandler()); // 6. 应用默认视图名 applyDefaultViewName(processedRequest, mv); // 7. 执行拦截器postHandle mappedHandler.applyPostHandle(processedRequest, response, mv); // 8. 处理渲染结果 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }这个过程中最易出问题的环节是HandlerMapping与HandlerAdapter的匹配。有次我们团队引入了一个第三方库它自带的Controller使用了特殊注解结果因为缺少对应的HandlerAdapter导致请求一直返回406错误。后来通过调试发现需要在配置中添加mvc:annotation-driven/来注册默认适配器。3.2 视图渲染的魔法时刻视图解析阶段藏着不少实用技巧。比如在JSP项目中可以配置多个ViewResolver形成解析链Configuration public class ViewConfig implements WebMvcConfigurer { Bean public ViewResolver jspViewResolver() { InternalResourceViewResolver resolver new InternalResourceViewResolver(); resolver.setPrefix(/WEB-INF/views/); resolver.setSuffix(.jsp); resolver.setOrder(2); return resolver; } Bean public ViewResolver excelViewResolver() { return new ExcelViewResolver(); // 自定义Excel视图解析器 } }这种配置下当控制器返回report视图名时系统会先尝试用ExcelViewResolver解析失败后再 fallback 到JSP解析器。我们在做报表导出功能时就利用了这个特性同一个URL通过参数控制返回HTML或Excel格式。4. 实战中的避坑指南4.1 拦截器与异步处理的陷阱在处理异步请求时拦截器的执行顺序会发生变化// 常规流程 preHandle → controller → postHandle → afterCompletion // 异步流程 preHandle → controller (开启异步) → afterConcurrentHandlingStarted → (异步线程) preHandle → controller → postHandle → afterCompletion曾经有个支付回调接口因为没注意这个差异导致日志记录不全。后来我们通过在拦截器中判断isAsyncStarted()来区分处理才解决了问题。4.2 文件上传的内存优化默认情况下文件上传会缓存到内存中。当处理大文件时这个机制可能引发OOM!-- 正确的配置方式 -- bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver property namemaxInMemorySize value51200/ !-- 50KB -- property namemaxUploadSize value52428800/ !-- 50MB -- /bean在云存储项目中我们更进一步实现了自定义的MultipartResolver直接将文件流式传输到OSS完全避免了本地磁盘缓存。DispatcherServlet的设计处处体现着Spring框架的匠心通过分层初始化保证扩展性采用策略模式支持灵活替换利用模板方法规范处理流程。理解这些设计思想比单纯记忆执行流程更有价值。当遇到复杂业务场景时这些底层认知能帮助你快速定位问题甚至开发出自己的定制组件。