SpringMVC全流程源码解析 前言 SpringMVC作为当今Java Web开发的事实标准,它优雅地屏蔽了底层Servlet API的复杂性,提供了一种声明式、注解驱动的开发模式。
1、体系概览——DispatcherServlet的继承体系 在阅读源码之前,我们必须先熟悉SpringMVC的核心——DispatcherServlet。它的继承体系设计得相当精妙,每一层都承担了特定的职责。
HttpServletBean:最底层的抽象类,直接继承自Java EE的HttpServlet。它的主要职责是 将Servlet的配置参数(例如contextConfigLocation)注入到Servlet本身的Bean属性中 。它重写了init()方法,利用BeanWrapper工具类将ServletConfig中的参数设置进去,为后续的Spring容器初始化做准备。
FrameworkServlet:这个抽象类完成了 Web层容器(WebApplicationContext)的初始化 。它维护了一个WebApplicationContext实例,即SpringMVC自己的容器。它重写了service()方法(以及doGet/doPost),将请求处理统一交给了模板方法processRequest()。在这个方法中,它处理了LocaleContext和RequestAttributes的初始化与清理,并最终调用了子类的doService()抽象方法。
**DispatcherServlet**:真正的“前端控制器”。它实现了FrameworkServlet中的doService()方法,并在其中定义了请求处理的宏观流程(如准备请求快照、调用doDispatch())。更重要的是,它管理着SpringMVC的九大核心组件,并在onRefresh()方法中调用initStrategies()来初始化这些组件。
这种层层递进的继承结构,完美体现了单一职责原则 和模板方法模式 。每一层只关心自己那一亩三分地,上层通过调用或重写下层的方法来完成更具体的功能。
2、SpringMVC容器的初始化 一个Web应用在启动时,SpringMVC是如何“活”起来的?关键在于DispatcherServlet的init()方法。
2.1 HttpServletBean.init():参数注入的起点 Servlet的生命周期始于init()方法。HttpServletBean重写了它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public final void init () throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'" ); } PropertyValues pvs = new HttpServletBean .ServletConfigPropertyValues(this , false ); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this ); ResourceLoader resourceLoader = new ServletContextResourceLoader (getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor (resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true ); initServletBean(); }
这里使用了BeanWrapper,这是Spring Be an操作的核心API。它意味着即使是Servlet本身,也被Spring当作一个Bean来对待。init-param中配置的contextConfigLocation就是这样被设置到FrameworkServlet的对应属性上的。
2.2 FrameworkServlet.initServletBean():容器诞生 initServletBean()是真正开始初始化Spring应用上下文的地方。
1 2 3 4 5 6 7 8 9 10 11 protected final void initServletBean () throws ServletException { try { this .webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } }
initWebApplicationContext()的逻辑非常关键,它涉及到Spring中常说的父子容器 概念。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 protected WebApplicationContext initWebApplicationContext () { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null ; if (this .webApplicationContext != null ) { wac = this .webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null ) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null ) { wac = findWebApplicationContext(); } if (wac == null ) { wac = createWebApplicationContext(rootContext); } return wac; }
父子容器之谜 :
根容器(Root Context) :通常由ContextLoaderListener加载,包含数据源、Service、DAO 等非Web相关的Bean。路径在web.xml的context-param中定义。
子容器(Servlet Context) :由DispatcherServlet加载,包含Controller、HandlerMapping、ViewResolver 等Web层相关的Bean。
关系 :子容器可以访问父容器的Bean(例如Controller可以注入Service),但父容器不能访问子容器的Bean。这种隔离保证了各层的职责清晰。
createWebApplicationContext方法会创建一个XmlWebApplicationContext(或注解版本),并将contextConfigLocation指向的配置文件加载进来,最后调用refresh()完成容器的启动。
2.3 DispatcherServlet.onRefresh():组件初始化 容器创建完毕后,会触发FrameworkServlet的onApplicationEvent,最终调用到DispatcherServlet的onRefresh()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected void onRefresh (ApplicationContext context) { initStrategies(context); }protected void initStrategies (ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
getDefaultStrategies()的秘密 :当Spring容器中没有任何HandlerMapping Bean时,SpringMVC会从DispatcherServlet.properties这个文件中加载默认配置。这个文件存在于Spring MVC的Jar包中,定义了如org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping等默认实现。这就是为什么即使你什么都不配置,SpringMVC也能工作的原因。
这九大组件是SpringMVC运转的基石。它们是如何被初始化的?
3、九大组件的初始化 initStrategies方法的代码非常清晰,它依次调用了九个初始化方法:
1 2 3 4 5 6 7 8 9 10 11 protected void initStrategies (ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
这九个方法虽然针对不同的组件类型,但其内部逻辑高度相似,都遵循着“先查找容器中已定义的Bean,如果没有则加载默认策略 ”的原则。下面我们逐一分析。
3.1 initMultipartResolver:文件上传解析器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private void initMultipartResolver (ApplicationContext context) { try { this .multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this .multipartResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this .multipartResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { this .multipartResolver = null ; if (logger.isTraceEnabled()) { logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared" ); } } }
关键点 :
MultipartResolver的Bean名称固定为multipartResolver(MULTIPART_RESOLVER_BEAN_NAME常量)。
与其他组件不同,它没有“默认策略”,如果容器中没有定义,multipartResolver属性直接为null。这是因为文件上传不是所有Web应用的必要功能,SpringMVC不会强制提供一个默认实现。当请求为multipart类型时,如果multipartResolver==null,则会抛出异常。
常用的实现类有CommonsMultipartResolver(基于Apache Commons FileUpload)和StandardServletMultipartResolver(基于Servlet 3.0 Part API)。
3.2 initLocaleResolver:本地化解析器 1 2 3 4 5 6 7 8 9 10 11 private void initLocaleResolver (ApplicationContext context) { try { this .localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); } catch (NoSuchBeanDefinitionException ex) { this .localeResolver = getDefaultStrategy(context, LocaleResolver.class); } }
关键点 :
LocaleResolver的Bean名称固定为localeResolver。
如果未定义,则调用getDefaultStrategy获取默认策略。getDefaultStrategy会从DispatcherServlet.properties文件中读取对应接口的默认实现类全限定名,并通过反射实例化。
默认实现为AcceptHeaderLocaleResolver,它根据请求头Accept-Language来确定Locale。
DispatcherServlet.properties片段 :
1 org.springframework.web.servlet.LocaleResolver =org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
3.3 initThemeResolver:主题解析器 主题解析器用于切换应用的UI主题,其初始化逻辑与LocaleResolver完全一致:
1 2 3 4 5 6 7 8 9 private void initThemeResolver (ApplicationContext context) { try { this .themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class); } catch (NoSuchBeanDefinitionException ex) { this .themeResolver = getDefaultStrategy(context, ThemeResolver.class); } }
默认实现是FixedThemeResolver,它总是返回一个固定的主题名(默认为”theme”),除非通过setDefaultThemeName修改。实际生产中,我们通常使用CookieThemeResolver或SessionThemeResolver来支持动态切换。
3.4 initHandlerMappings:处理器映射器 HandlerMapping负责将请求映射到具体的处理器(Handler)以及拦截器链。其初始化逻辑稍显复杂,因为支持多个HandlerMapping实例并存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private void initHandlerMappings (ApplicationContext context) { this .handlerMappings = null ; if (this .detectAllHandlerMappings) { Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true , false ); if (!matchingBeans.isEmpty()) { this .handlerMappings = new ArrayList <>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this .handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this .handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { } } if (this .handlerMappings == null ) { this .handlerMappings = getDefaultStrategies(context, HandlerMapping.class); } }
关键点 :
detectAllHandlerMappings默认为true,这意味着如果你在容器中定义了多个HandlerMapping(例如BeanNameUrlHandlerMapping和RequestMappingHandlerMapping),它们都会被收集并按优先级排序后使用。
默认策略中包含了两个经典的HandlerMapping:
RequestMappingHandlerMapping:支持@RequestMapping注解,是注解驱动MVC的核心。
BeanNameUrlHandlerMapping:将URL与以“/”开头的Bean名称映射。
1 2 org.springframework.web.servlet.HandlerMapping =org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
我们具体来了解下两个RequestMappingHandlerMapping和BeanNameUrlHandlerMapping的初始化实现:
3.4.1 RequestMappingHandlerMapping初始化逻辑 RequestMappingHandlerMapping 是 Spring MVC 中基于注解的请求映射的核心实现。它负责处理 @RequestMapping(以及其派生注解如 @GetMapping、@PostMapping 等),将控制器中的方法封装为 HandlerMethod,并与请求条件(如 URL、HTTP 方法、参数、头信息等)关联起来,形成一个映射注册表。当请求到达时,它能够根据请求信息快速定位到对应的 HandlerMethod。
下面我们从源码层面深入剖析它的实现细节。
1. 继承体系 RequestMappingHandlerMapping 的继承关系较为复杂,每一层都承担着不同的职责:
1 2 3 4 public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping , EmbeddedValueResolverAware { }
AbstractHandlerMapping:提供基础的 HandlerMapping 骨架,例如获取处理器、应用拦截器、处理 CORS 配置等。
AbstractHandlerMethodMapping<T>:抽象了基于方法的映射逻辑。它维护了一个映射注册表 MappingRegistry,并提供模板方法供子类实现如何从方法中提取映射条件(getMappingForMethod)以及如何匹配请求(getMatchingMapping)。
RequestMappingInfoHandlerMapping:填充了泛型参数 T 为 RequestMappingInfo,专门处理 RequestMappingInfo 类型的映射条件。
RequestMappingHandlerMapping:最终实现类,负责从带有 @RequestMapping 的方法中构建 RequestMappingInfo,并处理一些具体的细节(如自定义 RequestCondition)。
下面这张图可以清晰地展示其层次结构:
RequestMappingHandlerMapping 实现了 InitializingBean 接口,因此它的初始化入口在 afterPropertiesSet() 方法中。但实际扫描逻辑在其父类 AbstractHandlerMethodMapping 中定义。
2. 入口:afterPropertiesSet() 1 2 3 4 5 6 @Override public void afterPropertiesSet () { super .afterPropertiesSet(); }
父类 AbstractHandlerMethodMapping 实现了此方法:
1 2 3 4 5 @Override public void afterPropertiesSet () { initHandlerMethods(); }
3. 初始化所有处理器方法:initHandlerMethods() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected void initHandlerMethods () { String[] beanNames = (this .detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null ; try { beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { } if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
关键点:
isHandler(beanType):判断一个 bean 是否是处理器。在 RequestMappingHandlerMapping 中,该方法被重写为检查 bean 类型上是否有 @Controller 或 @RequestMapping 注解。
1 2 3 4 5 @Override protected boolean isHandler (Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
只有符合条件的 bean 才会被进一步扫描其内部的方法。
4. 检测处理器方法:detectHandlerMethods() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 protected void detectHandlerMethods (Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null ) { Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType)); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
getMappingForMethod(method, userType) 是一个模板方法,由子类 RequestMappingHandlerMapping 实现,负责从方法上提取 @RequestMapping 信息,构建 RequestMappingInfo 对象。
最后调用 registerHandlerMethod 将映射信息注册到内部的 MappingRegistry 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override protected RequestMappingInfo getMappingForMethod (Method method, Class<?> handlerType) { RequestMappingInfo info = null ; RequestMapping methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class); if (methodAnnotation != null ) { info = createRequestMappingInfo(methodAnnotation, method); RequestMapping typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, RequestMapping.class); if (typeAnnotation != null ) { info = createRequestMappingInfo(typeAnnotation, null ).combine(info); } } return info; }
createRequestMappingInfo 方法将注解属性转换为 RequestMappingInfo.Builder 并构建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private RequestMappingInfo createRequestMappingInfo (RequestMapping annotation, Method method) { RequestCondition<?> condition = (method != null ? getCustomMethodCondition(method) : getCustomTypeCondition(annotation.targetClass())); return RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(annotation.path())) .methods(annotation.method()) .params(annotation.params()) .headers(annotation.headers()) .consumes(annotation.consumes()) .produces(annotation.produces()) .mappingName(annotation.name()) .customCondition(condition) .options(this .config) .build(); }
RequestMappingInfo 封装了请求匹配所需的所有条件:
patternsCondition:URL 路径模式(支持 Ant 风格)
methodsCondition:HTTP 方法(GET, POST 等)
paramsCondition:请求参数条件
headersCondition:请求头条件
consumesCondition:Content-Type 条件
producesCondition:Accept 条件
customCondition:自定义扩展条件
6. 注册处理器方法:registerHandlerMethod() 1 2 3 protected void registerHandlerMethod (Object handler, Method method, T mapping) { this .mappingRegistry.register(mapping, handler, method); }
MappingRegistry 是 AbstractHandlerMethodMapping 的内部类,它持有两个核心映射:
mappingLookup:从 T(即 RequestMappingInfo)到 HandlerMethod 的映射。
urlLookup:从路径模式到 List<T> 的映射,用于快速通过 URL 查找可能的映射。
还有 registry 用于保存注册信息。
1 2 3 4 5 6 class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap <>(); private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap <>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap <>(); }
注册时,会解析 RequestMappingInfo 中的所有路径模式,将当前映射与每个路径关联起来,存入 urlLookup。
至此,初始化阶段完成,所有 @RequestMapping 方法都已准备就绪,等待请求的到来。
3.4.2 BeanNameUrlHandlerMapping初始化逻辑 BeanNameUrlHandlerMapping 是 Spring MVC 早期版本中提供的一种基于 Bean 名称 进行请求映射的 HandlerMapping 实现。它将容器中那些 名称以斜杠(/)开头 的 Bean 自动注册为 URL 路径的处理器。尽管如今注解驱动(@RequestMapping)已成为主流,但理解它的实现原理有助于我们深入掌握 Spring MVC 的映射机制以及 HandlerMapping 的设计思想。
1. 继承体系 1 2 3 public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { }
AbstractHandlerMapping:提供 HandlerMapping 的基础骨架,包含拦截器解析、CORS 配置等通用逻辑。
AbstractUrlHandlerMapping:继承自 AbstractHandlerMapping,专门处理基于 URL 的映射。它维护了一个 handlerMap(Map<String, Object>),用于存储 URL 路径到处理器(Bean 实例或 Bean 名称)的映射,并提供了基于 PathMatcher 的路径匹配算法。
AbstractDetectingUrlHandlerMapping:继承自 AbstractUrlHandlerMapping,增加了自动检测容器中所有 Bean 并注册 URL 的能力。它定义了一个模板方法 detectHandlers(),子类只需实现 determineUrlsForHandler(String beanName) 方法,返回该 Bean 对应的 URL 数组。
BeanNameUrlHandlerMapping:最终实现类,仅需实现 determineUrlsForHandler,将 Bean 名称和别名中以 / 开头的字符串作为 URL 返回。
2. detectHandlers():扫描容器中所有 Bean BeanNameUrlHandlerMapping 实现了 ApplicationContextAware 接口(通过父类),并通常在容器启动时由 DispatcherServlet 的 initHandlerMappings 触发初始化。它的初始化入口位于 AbstractDetectingUrlHandlerMapping 的 initApplicationContext() 方法(该方法在 Spring 容器刷新时被调用)。
1 2 3 4 5 6 @Override public void initApplicationContext () throws BeansException { super .initApplicationContext(); detectHandlers(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected void detectHandlers () throws BeansException { ApplicationContext applicationContext = obtainApplicationContext(); String[] beanNames = (this .detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class)); for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { registerHandler(urls, beanName); } } }
3. determineUrlsForHandler(beanName):提取 URL BeanNameUrlHandlerMapping 中的实现非常简洁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList <>(); if (beanName.startsWith("/" )) { urls.add(beanName); } String[] aliases = obtainApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/" )) { urls.add(alias); } } return StringUtils.toStringArray(urls); }
注意 :这里仅检查名称是否以 / 开头,不会进行通配符或模式处理。但注册后的 URL 可以是 Ant 风格模式(如 /user/*),因为 Bean 名称本身就支持 /user/* 这样的写法。
4. registerHandler(urls, beanName):存入映射表 AbstractUrlHandlerMapping 的 registerHandler 方法将每个 URL 与处理器关联:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 protected void registerHandler (String urlPath, Object handler) { Object resolvedHandler = handler; if (!this .lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } Object mappedHandler = this .handlerMap.get(urlPath); if (mappedHandler != null ) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException ("..." ); } } else { if (urlPath.equals("/" )) { setRootHandler(resolvedHandler); } else if (urlPath.equals("/*" )) { setDefaultHandler(resolvedHandler); } else { this .handlerMap.put(urlPath, resolvedHandler); } } }
handlerMap 的键是 URL 模式(可能是精确路径或 Ant 模式),值可以是:
Bean 实例(如果 lazyInitHandlers=false 且 Bean 是单例)
Bean 名称(String 类型,用于懒加载)
至此,初始化阶段完成,handlerMap 中已包含了所有以 / 开头的 Bean 名称到 URL 的映射。
3.5 initHandlerAdapters:处理器适配器 HandlerAdapter的职责是调用具体的处理器(Handler),屏蔽不同处理器类型的差异。其初始化逻辑与HandlerMapping几乎相同:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void initHandlerAdapters (ApplicationContext context) { this .handlerAdapters = null ; if (this .detectAllHandlerAdapters) { Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true , false ); if (!matchingBeans.isEmpty()) { this .handlerAdapters = new ArrayList <>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this .handlerAdapters); } } else { } if (this .handlerAdapters == null ) { this .handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); } }
默认策略 中包含了三个适配器:
1 2 3 org.springframework.web.servlet.HandlerAdapter =org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
HttpRequestHandlerAdapter:支持HttpRequestHandler类型的处理器(常用于远程导出等场景)。
SimpleControllerHandlerAdapter:支持实现了Controller接口的旧式控制器。
RequestMappingHandlerAdapter:支持@RequestMapping注解的方法,是最常用的适配器。
3.6 initHandlerExceptionResolvers:异常解析器 异常解析器负责在处理器执行抛出异常后,将异常解析为合适的ModelAndView。初始化逻辑也是查找容器中的所有HandlerExceptionResolver,若没有则使用默认策略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void initHandlerExceptionResolvers (ApplicationContext context) { this .handlerExceptionResolvers = null ; if (this .detectAllHandlerExceptionResolvers) { Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true , false ); if (!matchingBeans.isEmpty()) { this .handlerExceptionResolvers = new ArrayList <>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this .handlerExceptionResolvers); } } else { } if (this .handlerExceptionResolvers == null ) { this .handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class); } }
默认策略 :
1 2 3 org.springframework.web.servlet.HandlerExceptionResolver =org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
ExceptionHandlerExceptionResolver:支持@ExceptionHandler注解,是自定义异常处理的主要方式。
ResponseStatusExceptionResolver:处理带有@ResponseStatus注解的异常。
DefaultHandlerExceptionResolver:处理Spring MVC内置的异常(如NoSuchRequestHandlingMethodException),并转换为对应的HTTP状态码(如400、404)。
3.7 initRequestToViewNameTranslator:请求到视图名的翻译器 当处理器返回的ModelAndView中未显式指定视图名时,RequestToViewNameTranslator会根据请求信息生成一个默认的视图名。其初始化逻辑简单:
1 2 3 4 5 6 7 8 9 private void initRequestToViewNameTranslator (ApplicationContext context) { try { this .viewNameTranslator = context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class); } catch (NoSuchBeanDefinitionException ex) { this .viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class); } }
默认实现 :DefaultRequestToViewNameTranslator,它会去除请求URL的上下文路径、前缀和后缀,返回剩余的路径部分作为视图名。例如,请求/app/users/list.do,可能被转换为users/list。
3.8 initViewResolvers:视图解析器 ViewResolver负责将逻辑视图名解析为具体的View对象。初始化逻辑与HandlerMapping类似,支持多个ViewResolver并存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void initViewResolvers (ApplicationContext context) { this .viewResolvers = null ; if (this .detectAllViewResolvers) { Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true , false ); if (!matchingBeans.isEmpty()) { this .viewResolvers = new ArrayList <>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this .viewResolvers); } } else { } if (this .viewResolvers == null ) { this .viewResolvers = getDefaultStrategies(context, ViewResolver.class); } }
默认策略 :
1 org.springframework.web.servlet.ViewResolver =org.springframework.web.servlet.view.InternalResourceViewResolver
InternalResourceViewResolver:支持JSP等内部资源视图,默认会将视图名前缀/WEB-INF/和后缀.jsp拼接成完整路径,并通过RequestDispatcher转发。
3.9 initFlashMapManager:FlashMap管理器 FlashMap用于在重定向时传递参数(通常结合RedirectAttributes使用)。FlashMapManager负责存储、检索和清理FlashMap。
1 2 3 4 5 6 7 8 9 private void initFlashMapManager (ApplicationContext context) { try { this .flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class); } catch (NoSuchBeanDefinitionException ex) { this .flashMapManager = getDefaultStrategy(context, FlashMapManager.class); } }
默认实现 :SessionFlashMapManager,它将FlashMap存储在HttpSession中,适用于大部分Web应用。另一个可选实现是CookieFlashMapManager(但较少使用)。
3.10 总结 通过以上九个初始化方法的源码解析,我们可以总结出SpringMVC组件初始化的几个核心原则:
优先使用容器中的Bean :允许开发者通过@Bean或XML配置覆盖默认组件,实现高度定制。
支持多组件共存 :对于HandlerMapping、HandlerAdapter、ViewResolver等,通过detectAll标志和排序机制,允许多个组件协同工作。
智能的默认策略 :如果开发者未提供任何配置,SpringMVC会从DispatcherServlet.properties中加载一组合理的默认实现,让应用“开箱即用”。
按需加载 :像MultipartResolver这样的非必需组件,如果没有配置,则直接置为null,避免不必要的资源消耗。
4、请求处理核心流程(doDispatch) 当一切准备就绪,一个HTTP请求的到来,将我们在第二章中初始化的所有组件串联起来。所有的请求最终都会汇聚到DispatcherServlet的核心方法——doDispatch中。
我们先看流程图,再逐一拆解:
4.1 DispatcherServlet.doService() 在进入doDispatch之前,doService方法做了一些前置和后置的“家务活”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 protected void doService (HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, Object> attributesSnapshot = null ; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap <>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this .cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this .localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this .themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); FlashMap inputFlashMap = this .flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null ) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap ()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this .flashMapManager); try { doDispatch(request, response); } finally { } }
4.2 DispatcherServlet.doDispatch() 这是整个SpringMVC中最核心的方法,我们逐行解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null ; boolean multipartRequestParsed = false ; ModelAndView mv = null ; Exception dispatchException = null ; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); if (mappedHandler == null ) { noHandlerFound(processedRequest, response); return ; } HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET" .equals(method); if (isGet || "HEAD" .equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest (request, response).checkNotModified(lastModified) && isGet) { return ; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return ; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return ; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException ("Handler dispatch failed" , err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }
这就是doDispatch的宏观流程。接下来,我们将深入到每一个核心步骤中去。
4.3 核心步骤1:getHandler() 在 DispatcherServlet 的核心处理流程 doDispatch 中,第一个关键步骤就是通过 getHandler(processedRequest) 获取当前请求对应的处理器执行链(HandlerExecutionChain)。
这个方法负责遍历所有已注册的 HandlerMapping,找到第一个能够处理该请求的映射,并返回一个封装了处理器和拦截器链的执行单元。下面我们从源码层面逐层拆解这个方法的完整逻辑。
4.3.1 DispatcherServlet.getHandler() 1 2 3 4 5 6 7 8 9 10 11 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 ; }
逻辑解析 :
handlerMappings 是 DispatcherServlet 持有的 HandlerMapping 列表,在初始化阶段通过 initHandlerMappings() 加载(包括自定义和默认的 HandlerMapping)。
遍历该列表,依次调用每个 HandlerMapping 的 getHandler(request) 方法。
一旦某个 HandlerMapping 返回非空的 HandlerExecutionChain,立即将其返回,不再继续后续的 HandlerMapping。
如果所有 HandlerMapping 都返回 null,则整个 getHandler 返回 null,后续由 noHandlerFound 处理(通常导致 404)。
这种“短路 ”的设计保证了请求处理的效率,同时也允许我们通过调整 HandlerMapping 的顺序来控制不同映射策略的优先级。
4.3.2 HandlerMapping.getHandler() 的通用实现:AbstractHandlerMapping HandlerMapping 是一个接口,其最核心的实现都继承自抽象类 AbstractHandlerMapping。该抽象类提供了获取处理器执行链的通用骨架,而将具体的处理器查找逻辑留给子类(如 RequestMappingHandlerMapping、BeanNameUrlHandlerMapping)通过 getHandlerInternal 模板方法实现。
1. AbstractHandlerMapping.getHandler() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Override public final HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null ) { handler = getDefaultHandler(); } if (handler == null ) { return null ; } if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = (this .corsConfigurationSource != null ? this .corsConfigurationSource.getCorsConfiguration(request) : null ); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
关键步骤详解 :
**getHandlerInternal(request)**:由子类实现,负责根据请求信息查找对应的处理器。例如:
RequestMappingHandlerMapping 会根据请求路径、方法等条件从注册表中匹配 HandlerMethod。
BeanNameUrlHandlerMapping 会根据请求路径查找 handlerMap 中匹配的 bean 名称或实例。
SimpleUrlHandlerMapping 也会根据路径映射返回处理器。
默认处理器 :setDefaultHandler() 可以配置一个全局默认处理器,当没有找到匹配时使用(例如用于统一处理 404)。
处理器名称解析 :某些 HandlerMapping(如 BeanNameUrlHandlerMapping)在 handlerMap 中可能存储的是 bean 名称(字符串),此时需要从容器中获取实际的 bean 实例。
构建执行链 :getHandlerExecutionChain(handler, request) 是核心方法,负责将处理器包装成 HandlerExecutionChain,并将所有适用的拦截器加入链中。
CORS 处理 :如果处理器配置了跨域规则,或者当前请求是一个预检请求(OPTIONS 方法),则需要对执行链进行额外包装,加入 CORS 相关的拦截器逻辑。
2. getHandlerExecutionChain():装配拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 protected HandlerExecutionChain getHandlerExecutionChain (Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain (handler)); String lookupPath = this .urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH); for (HandlerInterceptor interceptor : this .adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this .pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
**adaptedInterceptors**:包含所有配置的拦截器,包括实现了 HandlerInterceptor 的 bean 以及通过 <mvc:interceptors> 配置的拦截器。其中,MappedInterceptor 是带有路径匹配规则的拦截器包装类。
路径匹配 :对于 MappedInterceptor,调用 matches 方法判断当前请求路径是否满足其包含或排除的路径模式。
执行链的构建 :最终返回的 HandlerExecutionChain 包含了处理器和一个有序的拦截器列表,在后续的 preHandle、postHandle、afterCompletion 阶段会依次调用。
4.3.3 子类 getHandlerInternal 的实现示例 为了更全面地理解 getHandler 的整体流程,我们选取两个典型子类,看看它们如何实现 getHandlerInternal。
1. AbstractHandlerMethodMapping.getHandlerInternal(RequestMappingHandlerMapping 的父类) 1 2 3 4 5 6 7 8 9 10 11 12 13 @Override protected HandlerMethod getHandlerInternal (HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); this .mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null ); } finally { this .mappingRegistry.releaseReadLock(); } }
lookupHandlerMethod 的详细逻辑已在之前章节介绍:它通过 mappingRegistry 根据路径、方法、参数等条件找到最匹配的 HandlerMethod。
2. AbstractUrlHandlerMapping.getHandlerInternal(BeanNameUrlHandlerMapping 的父类) 1 2 3 4 5 6 7 8 9 10 11 12 13 @Override protected Object getHandlerInternal (HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); Object handler = lookupHandler(lookupPath, request); if (handler == null ) { handler = getRootHandler(); if (handler == null ) { handler = getDefaultHandler(); } } return handler; }
lookupHandler 方法会先在 handlerMap 中查找精确匹配,如果没有则通过 PathMatcher 进行模式匹配,并选择最具体的处理器。其内部还处理了 URI 模板变量的提取。
4.4 CORS 预检请求的特殊处理 在 AbstractHandlerMapping.getHandler 的最后部分,有一段针对 CORS 的逻辑:
1 2 3 4 if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = ... executionChain = getCorsHandlerExecutionChain(request, executionChain, config); }
预检请求 :浏览器在发送实际跨域请求之前,会先发送一个 OPTIONS 请求,携带 Origin 和 Access-Control-Request-Method 头,询问服务器是否允许。Spring MVC 会为此类请求专门创建一个 PreFlightHandler 或通过 CorsInterceptor 处理,直接返回允许的跨域头,而不需要执行实际的处理器。
**getCorsHandlerExecutionChain**:如果当前请求是预检请求,或者处理器配置了跨域规则,该方法会返回一个特殊的执行链,其中可能包含 CorsInterceptor,或者在预检请求时直接返回一个处理 OPTIONS 的内置处理器。
4.5 没有找到处理器的情况 如果所有 HandlerMapping 都返回 null,DispatcherServlet.getHandler 返回 null。随后在 doDispatch 中会调用 noHandlerFound 方法:
1 2 3 4 5 6 7 8 protected void noHandlerFound (HttpServletRequest request, HttpServletResponse response) throws Exception { if (this .throwExceptionIfNoHandlerFound) { throw new NoHandlerFoundException (request.getMethod(), getRequestUri(request), new ServletServerHttpRequest (request).getHeaders()); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND); } }
根据配置 throwExceptionIfNoHandlerFound(默认为 false),返回 404 错误页面或抛出异常(可由异常解析器处理)。
4.4 核心步骤2:getHandlerAdapter() 为什么需要HandlerAdapter?因为SpringMVC可以处理多种类型的Handler(例如实现Controller接口的旧式Handler、HttpRequestHandler、以及最常用的注解HandlerMethod)。DispatcherServlet不希望直接依赖具体的Handler类型,而是通过适配器来统一调用。
1 2 3 4 5 6 7 8 9 10 11 12 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 ..." ); }
supports方法的逻辑各有不同。对于RequestMappingHandlerAdapter,它检查传入的handler是否是HandlerMethod类型。
在 DispatcherServlet 的核心处理流程 doDispatch 中,成功获取到处理器执行链(HandlerExecutionChain)之后,紧接着的步骤就是通过 getHandlerAdapter(mappedHandler.getHandler()) 获取能够处理该处理器的 HandlerAdapter。这个方法虽然代码量不大,但承担着将 DispatcherServlet 与具体处理器调用方式解耦的关键职责。下面我们从源码层面深入剖析其执行逻辑。
4.4.1 DispatcherServlet.getHandlerAdapter() 1 2 3 4 5 6 7 8 9 10 11 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 + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler" ); }
逻辑解析 :
遍历 handlerAdapters 列表 :handlerAdapters 是 DispatcherServlet 持有的 HandlerAdapter 列表,在初始化阶段通过 initHandlerAdapters() 加载(同样支持自定义和默认的 adapter)。
**调用 supports(handler)**:对列表中的每个 adapter,调用其 supports 方法,判断该 adapter 是否能处理传入的 handler 对象。
返回第一个匹配的 adapter :一旦找到支持当前 handler 的 adapter,立即返回,不再继续遍历。
若未找到,抛出异常 :如果遍历完所有 adapter 都没有找到支持的,抛出 ServletException,提示配置中缺少合适的 HandlerAdapter。
这个方法的逻辑简洁明了,但背后的设计思想却十分重要——适配器模式 的典型应用。DispatcherServlet 不关心 handler 的具体类型(可能是 HandlerMethod、Controller 实例、HttpRequestHandler 等),只需要找到一个能“适配”它的 adapter 即可。
4.4.2 handlerAdapters 列表的来源 handlerAdapters 列表的初始化与 handlerMappings 类似,都在 DispatcherServlet.initHandlerAdapters() 方法中完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private void initHandlerAdapters (ApplicationContext context) { this .handlerAdapters = null ; if (this .detectAllHandlerAdapters) { Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true , false ); if (!matchingBeans.isEmpty()) { this .handlerAdapters = new ArrayList <>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this .handlerAdapters); } } else { try { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this .handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { } } if (this .handlerAdapters == null ) { this .handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); } }
默认情况下,detectAllHandlerAdapters 为 true,因此 Spring 会收集容器中所有 HandlerAdapter 类型的 Bean,并按 @Order 或 Ordered 接口排序。若没有自定义任何 HandlerAdapter,则从 DispatcherServlet.properties 中加载默认的三个适配器:
1 2 3 org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
这三个适配器覆盖了最常见的处理器类型:
HttpRequestHandlerAdapter:支持 HttpRequestHandler
SimpleControllerHandlerAdapter:支持实现了 Controller 接口的处理器
RequestMappingHandlerAdapter:支持 HandlerMethod(即注解 @RequestMapping 的方法)
4.4.3 各 HandlerAdapter 的 supports 逻辑剖析 1. HttpRequestHandlerAdapter.supports() 1 2 3 4 @Override public boolean supports (Object handler) { return (handler instanceof HttpRequestHandler); }
仅当处理器是 HttpRequestHandler 的实例时才支持。这种处理器通常用于直接操作 HttpServletResponse 输出流,如静态资源处理、文件下载等。
2. SimpleControllerHandlerAdapter.supports() 1 2 3 4 @Override public boolean supports (Object handler) { return (handler instanceof Controller); }
仅当处理器实现了 org.springframework.web.servlet.mvc.Controller 接口时才支持。这是 Spring MVC 早期版本中常用的控制器接口,其 handleRequest 方法返回 ModelAndView。
3. RequestMappingHandlerAdapter.supports() 1 2 3 4 @Override public boolean supports (Object handler) { return (handler instanceof HandlerMethod); }
仅当处理器是 HandlerMethod 的实例时才支持。HandlerMethod 封装了带有 @RequestMapping 注解的方法及其所属的 Bean,是注解驱动控制器的核心。
4.4.4 适配器模式的设计意图 通过 getHandlerAdapter 方法,DispatcherServlet 实现了对多种处理器类型的统一调用:
如果没有适配器模式 ,DispatcherServlet 将不得不编写大量的 if-else 或 switch 语句,针对每种处理器类型分别调用其特定的处理方法(如 HttpRequestHandler.handleRequest()、Controller.handleRequest()、HandlerMethod.invokeAndHandle())。这不仅使代码臃肿,而且难以扩展新的处理器类型。
有了适配器模式 ,每种处理器类型都有对应的适配器,将不同风格的调用接口统一为 HandlerAdapter 的 handle 方法。DispatcherServlet 只需调用 adapter.handle(request, response, handler) 即可,完全无需关心 handler 的具体类型。这就是“面向接口编程 ”的经典实践。
4.4.5 异常情况分析 如果 getHandlerAdapter 抛出 ServletException,意味着当前 handler 的类型没有任何一个已注册的 HandlerAdapter 能够处理。可能的原因有:
**自定义了新的处理器类型,却没有提供对应的 HandlerAdapter**:例如,你编写了一个实现某个自定义接口的处理器,但忘记注册对应的 adapter。
**误删了默认的 HandlerAdapter**:如果你在容器中覆盖了所有默认 adapter,但新的 adapter 集合未能覆盖所有实际使用的处理器类型,就会导致某些请求失败。
handler 对象本身不合法 :例如通过 HandlerMapping 返回的 handler 是 null 或意外类型,但这种情况通常会在更早的环节被处理。
该异常最终会由 DispatcherServlet 的异常处理机制捕获,通常导致 500 错误。因此,在开发自定义处理器时,务必确保有相应的 HandlerAdapter 与之匹配。
4.4.6 getHandlerAdapter 在整体流程中的位置 回顾 DispatcherServlet.doDispatch 的核心代码片段:
1 2 3 4 5 6 7 8 9 10 11 mappedHandler = getHandler(processedRequest);if (mappedHandler == null ) { noHandlerFound(processedRequest, response); return ; }HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
由此可见,getHandlerAdapter 是连接“寻找处理器”与“执行处理器”的桥梁。它确保了无论 handler 是何种形态,都能被正确地驱动。
4.5 核心步骤3:HandlerAdapter.handle() 这一步骤的核心在RequestMappingHandlerAdapter中,它最终会调用到ServletInvocableHandlerMethod。这里的重点是参数解析器(HandlerMethodArgumentResolver) 和返回值处理器(HandlerMethodReturnValueHandler) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 protected ModelAndView handleInternal (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { mav = invokeHandlerMethod(request, response, handlerMethod); }protected ModelAndView invokeHandlerMethod (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletWebRequest webRequest = new ServletWebRequest (request, response); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this .argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this .returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); ModelAndViewContainer mavContainer = new ModelAndViewContainer (); invocableMethod.invokeAndHandle(webRequest, mavContainer); return getModelAndView(mavContainer, modelFactory, webRequest); }
最激动人心的时刻发生在invocableMethod.invokeAndHandle()中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public void invokeAndHandle (ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); try { this .returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } }public Object invokeForRequest (NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); return doInvoke(args); }private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object [parameters.length]; for (int i = 0 ; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; if (this .argumentResolvers.supportsParameter(parameter)) { try { args[i] = this .argumentResolvers.resolveArgument(parameter, mavContainer, request, this .dataBinderFactory); continue ; } catch (Exception ex) { } } } return args; }
这就是@RequestParam、@RequestBody、@PathVariable等注解工作的底层原理 。每个解析器(如RequestParamMethodArgumentResolver)负责从request的Parameter、Body或URI模板变量中读取数据,并进行类型转换,最终将转换后的值注入到Controller方法的参数列表中。
4.5.1 HandlerAdapter 接口定义 1 2 3 4 5 6 7 8 public interface HandlerAdapter { boolean supports (Object handler) ; @Nullable ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; long getLastModified (HttpServletRequest request, Object handler) ; }
handle 方法接收 request、response 以及之前通过 HandlerMapping 获取的 handler 对象,返回一个 ModelAndView(可能为 null)。该方法声明抛出 Exception,意味着处理器执行过程中抛出的任何异常都会向上传递,由 DispatcherServlet 的异常处理机制捕获。
4.5.2 RequestMappingHandlerAdapter.handle 执行逻辑 RequestMappingHandlerAdapter 是处理注解控制器(即 @RequestMapping 方法)的核心适配器。其 handle 方法最终会调用到目标方法,并处理参数解析、数据绑定、返回值处理等复杂逻辑。下面是整个调用链的逐步剖析。
1. 入口方法:handle 1 2 3 4 5 @Override public final ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); }
handle 方法直接将 handler 强制转换为 HandlerMethod,然后调用内部方法 handleInternal。
2. handleInternal:准备与调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 protected ModelAndView handleInternal (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { checkRequest(request); ModelAndView mav; if (this .synchronizeOnSession) { HttpSession session = request.getSession(false ); if (session != null ) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this .cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
关键点 :
checkRequest 会验证请求方法(如是否在 @RequestMapping 中允许的方法),如果不允许会抛出 HttpRequestMethodNotSupportedException。
synchronizeOnSession 是配置项,默认为 false,主要用于需要在 Session 级别同步的场景(例如避免并发修改 Session 属性)。
核心是 invokeHandlerMethod,它完成实际的方法调用。
3. invokeHandlerMethod:方法调用的核心 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 protected ModelAndView invokeHandlerMethod (HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest (request, response); WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this .argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this .returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this .parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer (); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this .ignoreDefaultModelOnRedirect); invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object [0 ]); return getModelAndView(mavContainer, modelFactory, webRequest); }
步骤分解 :
**创建 ServletWebRequest**:包装原生 request/response,方便后续访问。
**getDataBinderFactory**:收集当前 HandlerMethod 及其所属 Controller 上标注的 @InitBinder 方法,创建一个 WebDataBinderFactory,用于后续参数绑定。
**getModelFactory**:收集 @ModelAttribute 方法(用于在调用目标方法前预先准备模型属性)以及 @SessionAttributes 配置,创建 ModelFactory。
**createInvocableHandlerMethod**:将 HandlerMethod 包装为可调用的 ServletInvocableHandlerMethod,这个类扩展了 HandlerMethod,增加了调用和参数解析能力。
设置参数解析器和返回值处理器 :argumentResolvers 和 returnValueHandlers 是 RequestMappingHandlerAdapter 初始化时装配好的两个重要组件列表,分别负责将请求数据转换为方法参数,以及处理方法返回值。
**initModel**:ModelFactory 会调用所有 @ModelAttribute 方法,将其返回值存入模型;同时从 session 中取出 @SessionAttributes 指定的属性放入模型,确保模型在调用前已包含所需数据。
**invokeAndHandle**:真正执行方法并处理返回值。
**getModelAndView**:根据调用后的 mavContainer 状态,构建 ModelAndView 对象。
4. invokeAndHandle:执行与返回值处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public void invokeAndHandle (ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null ) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true ); return ; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true ); return ; } mavContainer.setRequestHandled(false ); Assert.state(this .returnValueHandlers != null , "No return value handlers" ); try { this .returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { throw ex; } }
核心流程 :
**invokeForRequest**:解析方法参数并执行方法,返回结果。这一步会用到 argumentResolvers。
**setResponseStatus**:如果方法或类上有 @ResponseStatus 注解,则设置 HTTP 响应状态码。
判断返回值 :
如果返回值为 null,且请求已被处理(例如直接向 response 写入了数据,或设置了 @ResponseStatus 的 reason),则将 mavContainer.setRequestHandled(true),告知上层无需渲染视图。
否则,调用 returnValueHandlers.handleReturnValue 处理返回值。
返回值处理器 :returnValueHandlers 会遍历所有已注册的 HandlerMethodReturnValueHandler,找到支持当前返回类型的处理器,并调用其 handleReturnValue 方法。例如:
ModelAndViewResolverMethodReturnValueHandler:处理返回 ModelAndView 的情况。
ViewMethodReturnValueHandler:处理返回 View 的情况。
ResponseBodyEmitterReturnValueHandler:处理 @ResponseBody 和 ResponseEntity 等,直接将数据写入 response,并标记 requestHandled = true。
ModelAttributeMethodProcessor:处理返回 @ModelAttribute 方法,将返回值添加到模型中,并可能选择视图。
5. invokeForRequest:参数解析与方法调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public Object invokeForRequest (NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } return doInvoke(args); }
getMethodArgumentValues 负责为每个方法参数找到合适的解析器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object [parameters.length]; for (int i = 0 ; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this .parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null ) { continue ; } if (!this .resolvers.supportsParameter(parameter)) { throw new IllegalStateException (formatArgumentError(parameter, "No suitable resolver" )); } try { args[i] = this .resolvers.resolveArgument(parameter, mavContainer, request, this .dataBinderFactory); } catch (Exception ex) { throw ex; } } return args; }
参数解析器(HandlerMethodArgumentResolver)实现了将请求中的信息(如 URL 参数、请求体、路径变量、Session 属性等)转换为方法参数值的逻辑。例如:
RequestParamMethodArgumentResolver:处理 @RequestParam。
PathVariableMethodArgumentResolver:处理 @PathVariable。
RequestBodyMethodArgumentResolver:处理 @RequestBody,使用 HttpMessageConverter 读取请求体。
ModelAttributeMethodProcessor:处理 @ModelAttribute,通过数据绑定将请求参数绑定到对象。
6. 返回 ModelAndView 在 invokeHandlerMethod 的最后,调用 getModelAndView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private ModelAndView getModelAndView (ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null ; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView (mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); RequestContextUtils.getOutputFlashMap(webRequest).putAll(flashAttributes); } return mav; }
至此,RequestMappingHandlerAdapter.handle 完成,返回 ModelAndView(或 null)。
4.5.3 RequestMappingHandlerAdapter.handle 执行逻辑 SimpleControllerHandlerAdapter 用于处理实现了 org.springframework.web.servlet.mvc.Controller 接口的处理器,其 handle 方法非常简单:
1 2 3 4 5 6 7 8 9 10 @Override public ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Controller controller = (Controller) handler; return controller.handleRequest(request, response); }
Controller 接口的定义如下:
1 2 3 public interface Controller { ModelAndView handleRequest (HttpServletRequest request, HttpServletResponse response) throws Exception; }
因此,适配器直接调用目标控制器的 handleRequest 方法,并原样返回其返回的 ModelAndView。没有额外的参数解析、数据绑定等复杂逻辑,完全由控制器自己处理。
4.5.4 HttpRequestHandlerAdapter.handle 执行逻辑 HttpRequestHandlerAdapter 用于处理 HttpRequestHandler,其 handle 方法同样简洁:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpRequestHandler handlerToUse = (HttpRequestHandler) handler; handlerToUse.handleRequest(request, response); return null ; }
由于 HttpRequestHandler 直接操作 HttpServletResponse(例如输出文件内容),Spring MVC 无需再渲染视图,因此返回 null。
4.5.5 handle 方法返回值的后续处理 在 doDispatch 中,handle 返回的 ModelAndView 被赋值给局部变量 mv:
1 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
如果返回 null,且不是异步请求,则会进入 processDispatchResult 方法。在 processDispatchResult 中,如果 mv != null 且 !mv.wasCleared(),则会调用 render(mv, request, response) 进行视图渲染;否则,直接返回响应(例如 @ResponseBody 的情况)。
对于 HttpRequestHandlerAdapter 返回 null 的场景,由于已经直接写入了响应,mv 为 null,因此 render 方法不会执行,请求结束。
对于 SimpleControllerHandlerAdapter 返回的 ModelAndView,如果包含视图名,则会经过视图解析和渲染。
4.5.6 异常处理 所有 handle 方法都声明抛出 Exception,因此处理器执行过程中抛出的任何异常都会直接向上传递。DispatcherServlet 在 doDispatch 中会捕获这些异常,并交给 processDispatchResult 中的 processHandlerException 处理,最终可能返回一个错误视图或直接输出错误响应。
4.6 核心步骤4:processDispatchResult() 当处理器执行完毕返回ModelAndView后,或者执行过程中抛出异常,都会由processDispatchResult方法来处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private void processDispatchResult (HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if (exception != null ) { if (exception instanceof ModelAndViewDefiningException) { mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null ); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null ); } } if (mv != null && !mv.wasCleared()) { render(mv, request, response); } else { } if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, null ); } }
**processHandlerException**:它会遍历所有的HandlerExceptionResolver实现,如ExceptionHandlerExceptionResolver(处理@ExceptionHandler注解)或DefaultHandlerExceptionResolver(处理Spring内置异常,如400、404)。找到第一个能处理该异常的Resolver,并返回一个包含错误页面或错误数据的ModelAndView。
render方法 :视图渲染的入口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected void render (ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = (this .localeResolver != null ? this .localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null ) { view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null ) { throw new ServletException ("Could not resolve view with name '" + mv.getViewName() + "'..." ); } } else { view = mv.getView(); if (view == null ) { throw new ServletException ("ModelAndView [" + mv + "] neither contains a view name nor a View object." ); } } view.render(mv.getModelInternal(), request, response); }
resolveViewName遍历所有的ViewResolver,调用其resolveViewName方法。例如,InternalResourceViewResolver会将”hello”解析为指向”/WEB-INF/hello.jsp”的InternalResourceView对象。最终,view.render()方法会进行RequestDispatcher.forward(request, response)操作,将请求转发给JSP去生成最终的HTML。
4.6.1 processDispatchResult 在 doDispatch 中的位置 先回顾一下 doDispatch 的末尾部分:
1 2 3 4 5 6 7 8 9 10 11 12 try { }catch (Exception ex) { dispatchException = ex; }catch (Throwable err) { dispatchException = new NestedServletException ("Handler dispatch failed" , err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
如果处理器执行过程中抛出异常,会被捕获并存入 dispatchException。
无论是否发生异常,最终都会调用 processDispatchResult 来处理结果。
该方法负责根据 dispatchException 是否存在来决定是处理异常还是正常渲染视图。
4.6.2 processDispatchResult 方法源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 private void processDispatchResult (HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if (exception != null ) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered" , exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null ); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null ); } } if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned." ); } } if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, exception); } }
4.6.3 分步深度解析 1. 异常处理分支 当 exception != null 时,说明处理器执行过程中抛出了异常。这里有两种情况:
**ModelAndViewDefiningException**:这是一个特殊的异常,它内部已经包含了一个 ModelAndView 对象。直接使用该异常自带的 ModelAndView 作为渲染目标。这种异常通常用于在拦截器或处理器中需要快速跳转到某个视图的场景(例如显示一个错误页面)。
其他异常 :调用 processHandlerException(request, response, handler, exception) 方法,让注册的 HandlerExceptionResolver 来处理异常。这是 Spring MVC 异常处理的核心机制,支持 @ExceptionHandler、@ResponseStatus 以及默认的 Spring 内部异常解析。
processHandlerException 内部逻辑1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 protected ModelAndView processHandlerException (HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { ModelAndView exMv = null ; if (this .handlerExceptionResolvers != null ) { for (HandlerExceptionResolver resolver : this .handlerExceptionResolvers) { exMv = resolver.resolveException(request, response, handler, ex); if (exMv != null ) { break ; } } } if (exMv != null ) { if (exMv.isEmpty()) { exMv.setViewName(getDefaultViewName(request)); } if (logger.isTraceEnabled()) { logger.trace("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }
关键点 :
handlerExceptionResolvers 列表包含所有实现了 HandlerExceptionResolver 的 Bean,如 ExceptionHandlerExceptionResolver(处理 @ExceptionHandler)、ResponseStatusExceptionResolver(处理 @ResponseStatus)、DefaultHandlerExceptionResolver(处理 Spring 内部异常)。
解析器一旦返回非空的 ModelAndView,即停止遍历。返回的 ModelAndView 可能为空(仅有模型,需要后续确定视图名),此时会调用 getDefaultViewName 设置默认视图名(通常基于请求路径)。
调用 WebUtils.exposeErrorRequestAttributes 将异常信息放入 request 属性(如 javax.servlet.error.exception、javax.servlet.error.message 等),供错误页面使用。
如果没有解析器处理该异常,则直接重新抛出,最终会导致容器返回 500 错误。
2. 视图渲染分支 在异常处理后(或没有异常的情况下),代码检查 mv 是否为 null 且没有被清除(wasCleared())。如果满足条件,则调用 render(mv, request, response) 进行视图渲染。
render 方法核心逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected void render (ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { Locale locale = (this .localeResolver != null ? this .localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null ) { view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null ) { throw new ServletException ("Could not resolve view with name '" + mv.getViewName() + "'..." ); } } else { view = mv.getView(); if (view == null ) { throw new ServletException ("ModelAndView [" + mv + "] neither contains a view name nor a View object." ); } } view.render(mv.getModelInternal(), request, response); }
resolveViewName 的细节 :
遍历所有已注册的 ViewResolver,按顺序调用其 resolveViewName 方法,直到返回一个非空的 View 对象。
常见的 ViewResolver 包括 InternalResourceViewResolver(解析 JSP)、BeanNameViewResolver(根据视图名查找同名的 Bean)、ContentNegotiatingViewResolver(根据请求 Accept 头协商视图)等。
视图解析器通常支持缓存,避免重复解析。
view.render 的职责 :
View 接口的核心方法是 render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)。
不同的 View 实现有不同的渲染逻辑:
InternalResourceView:将请求转发给 JSP 资源(RequestDispatcher.forward)。
RedirectView:发送重定向响应(response.sendRedirect)。
MappingJackson2JsonView:将模型数据序列化为 JSON 并写入响应体。
渲染过程中,模型数据会被暴露给视图(如作为 request 属性)。
注意 :如果 mv 的 wasCleared() 返回 true,说明处理器(或框架)明确指示不需要渲染视图,此时会跳过渲染步骤。
3. 拦截器 afterCompletion 回调 无论是否发生异常,也无论是否渲染视图,最终都会执行:
1 2 3 if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, exception); }
triggerAfterCompletion 会逆序调用拦截器链中所有拦截器的 afterCompletion 方法(即与 preHandle 执行的顺序相反),并传入最初捕获的 exception(即 dispatchException)。这允许拦截器在请求完全结束后进行资源清理、日志记录等操作。
设计思想 :afterCompletion 是拦截器生命周期的最后阶段,确保无论请求成功与否,都能得到执行,类似于 finally 块。
4.6.4 异常处理与正常渲染的协作 processDispatchResult 巧妙地将异常处理和正常渲染统一起来:
如果存在异常,先通过异常解析器将其转换为 ModelAndView(可能是一个错误页面)。
然后将这个 ModelAndView 像正常结果一样传递给渲染逻辑。
这样,异常处理和正常请求的处理共享同一套渲染机制,代码简洁且一致。
4.6.5 特殊情况处理 1. 没有 ModelAndView 的情况 如果 mv == null(例如 HttpRequestHandler 直接写入响应,或 @ResponseBody 方法),则跳过渲染步骤。但拦截器的 afterCompletion 仍然会执行。
2. 异常处理过程中未找到视图 如果在 processHandlerException 中解析器返回的 ModelAndView 没有视图名且没有视图对象,则会尝试设置默认视图名;如果连默认视图名都无法解析,最终会在 render 阶段抛出异常,导致容器返回 500 错误。
3. 多次异常处理 注意:如果在 render 过程中抛出异常,该异常不会被 processDispatchResult 捕获,而是会直接抛出,最终由 Servlet 容器处理。这是因为 render 已经是请求处理的最后一步,无法再交给异常解析器处理(否则可能陷入无限循环)。Spring MVC 的设计选择是在渲染阶段发生错误时,直接让容器处理。
5、拦截器的完整生命周期 通过上面的流程,我们可以看到拦截器(HandlerInterceptor)的三个方法分别在请求生命周期的不同阶段被调用:
preHandle:在HandlerAdapter执行处理器 之前 执行。如果返回false,DispatcherServlet将不再执行后续的处理器和postHandle,但会执行已执行过的拦截器的afterCompletion(逆序)。
postHandle:在处理器执行之后、processDispatchResult渲染视图 之前 执行。此时可以修改ModelAndView。
afterCompletion:在processDispatchResult执行完毕、整个请求处理完成 之后 执行。通常用于资源清理。
这种设计为我们在不侵入业务代码的前提下,提供了强大的AOP式横切关注点(如日志、权限、性能监控)的支持。
六、总结 DispatcherServlet从一个init参数开始,逐步构建起一个庞大的九大组件生态;看到了一个HTTP请求如何被一步步拆解、匹配、适配、反射调用,最终渲染成响应;参数解析器如何将网络数据和对象模型无缝转换;也看到了拦截器如何在关键节点上织入横切逻辑。
理解这些源码,不仅能帮助我们解决线上疑难杂症(如参数绑定失败、拦截器不生效),更能让我们在未来的架构设计中,借鉴这种高内聚、低耦合、面向接口、关注点分离 的哲学。