SpringMVC全流程源码解析

SpringMVC全流程源码解析

前言

​ SpringMVC作为当今Java Web开发的事实标准,它优雅地屏蔽了底层Servlet API的复杂性,提供了一种声明式、注解驱动的开发模式。

1、体系概览——DispatcherServlet的继承体系

在阅读源码之前,我们必须先熟悉SpringMVC的核心——DispatcherServlet。它的继承体系设计得相当精妙,每一层都承担了特定的职责。

dispatcherServlet

  1. HttpServletBean:最底层的抽象类,直接继承自Java EE的HttpServlet。它的主要职责是将Servlet的配置参数(例如contextConfigLocation)注入到Servlet本身的Bean属性中。它重写了init()方法,利用BeanWrapper工具类将ServletConfig中的参数设置进去,为后续的Spring容器初始化做准备。
  2. FrameworkServlet:这个抽象类完成了Web层容器(WebApplicationContext)的初始化。它维护了一个WebApplicationContext实例,即SpringMVC自己的容器。它重写了service()方法(以及doGet/doPost),将请求处理统一交给了模板方法processRequest()。在这个方法中,它处理了LocaleContextRequestAttributes的初始化与清理,并最终调用了子类的doService()抽象方法。
  3. **DispatcherServlet**:真正的“前端控制器”。它实现了FrameworkServlet中的doService()方法,并在其中定义了请求处理的宏观流程(如准备请求快照、调用doDispatch())。更重要的是,它管理着SpringMVC的九大核心组件,并在onRefresh()方法中调用initStrategies()来初始化这些组件。

这种层层递进的继承结构,完美体现了单一职责原则模板方法模式。每一层只关心自己那一亩三分地,上层通过调用或重写下层的方法来完成更具体的功能。

2、SpringMVC容器的初始化

一个Web应用在启动时,SpringMVC是如何“活”起来的?关键在于DispatcherServletinit()方法。

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
// org.springframework.web.servlet.HttpServletBean
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}

// 1. 将ServletConfig中的参数封装到PropertyValues中
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this, false);
// ...

// 2. 将当前Servlet自身包装成一个BeanWrapper,以便进行Spring风格的属性注入
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 3. 加载并注册自定义属性编辑器(如果有配置)
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 4. 留给子类扩展,初始化BeanWrapper
initBeanWrapper(bw);
// 5. 执行属性注入!将web.xml里配置的<init-param>注入到当前Servlet的字段中
bw.setPropertyValues(pvs, true);
// ...

// 6. 留给子类扩展的钩子方法,实际上FrameworkServlet就是在这里开始初始化容器的
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
// org.springframework.web.servlet.FrameworkServlet
protected final void initServletBean() throws ServletException {
// ...
try {
// 核心方法:初始化WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
// 模板方法:留给DispatcherServlet去实现,用于初始化框架特有的策略(如九大组件)
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
// org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext initWebApplicationContext() {
// 1. 获取根容器(Root WebApplicationContext)
// 这是通过ContextLoaderListener在ServletContext启动时创建的
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;

// 2. 如果已经在构造方法中传入了WebApplicationContext,则直接使用(较少见)
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);
}
}
}
// 3. 如果未通过构造传入,尝试通过ServletContext的属性查找已有的上下文(如通过ContextLoaderListener创建的)
if (wac == null) {
wac = findWebApplicationContext();
}
// 4. 如果还是找不到,就创建一个全新的!
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}

// ... 触发刷新事件等
return wac;
}

父子容器之谜

  • 根容器(Root Context):通常由ContextLoaderListener加载,包含数据源、Service、DAO等非Web相关的Bean。路径在web.xmlcontext-param中定义。
  • 子容器(Servlet Context):由DispatcherServlet加载,包含Controller、HandlerMapping、ViewResolver等Web层相关的Bean。
  • 关系:子容器可以访问父容器的Bean(例如Controller可以注入Service),但父容器不能访问子容器的Bean。这种隔离保证了各层的职责清晰。

createWebApplicationContext方法会创建一个XmlWebApplicationContext(或注解版本),并将contextConfigLocation指向的配置文件加载进来,最后调用refresh()完成容器的启动。

2.3 DispatcherServlet.onRefresh():组件初始化

容器创建完毕后,会触发FrameworkServletonApplicationEvent,最终调用到DispatcherServletonRefresh()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// org.springframework.web.servlet.DispatcherServlet
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

// 初始化所有策略
protected 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. 初始化FlashMap管理器
}

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 {
// 从容器中查找类型为 MultipartResolver 的 Bean
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) {
// 如果没有找到,则设置为 null,后续处理中会检查并跳过文件上传解析
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}

关键点

  • MultipartResolver的Bean名称固定为multipartResolverMULTIPART_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 {
// 从容器中查找类型为 LocaleResolver 的 Bean(不限制名称)
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修改。实际生产中,我们通常使用CookieThemeResolverSessionThemeResolver来支持动态切换。

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;

// 是否查找容器中所有的 HandlerMapping(默认为 true)
if (this.detectAllHandlerMappings) {
// 从容器中找出所有 HandlerMapping 类型的 Bean,包括祖先容器中的
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 按照 @Order 注解或 Ordered 接口排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
// 只查找指定名称的 HandlerMapping
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException ex) {
// 忽略
}
}

// 如果仍然没有找到任何 HandlerMapping,则使用默认策略(从 properties 中加载)
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
// ...
}
}

关键点

  • detectAllHandlerMappings默认为true,这意味着如果你在容器中定义了多个HandlerMapping(例如BeanNameUrlHandlerMappingRequestMappingHandlerMapping),它们都会被收集并按优先级排序后使用。
  • 默认策略中包含了两个经典的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

我们具体来了解下两个RequestMappingHandlerMappingBeanNameUrlHandlerMapping的初始化实现:

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:填充了泛型参数 TRequestMappingInfo,专门处理 RequestMappingInfo 类型的映射条件。
  • RequestMappingHandlerMapping:最终实现类,负责从带有 @RequestMapping 的方法中构建 RequestMappingInfo,并处理一些具体的细节(如自定义 RequestCondition)。

下面这张图可以清晰地展示其层次结构:

RequestMappingHandlerMapping

RequestMappingHandlerMapping 实现了 InitializingBean 接口,因此它的初始化入口在 afterPropertiesSet() 方法中。但实际扫描逻辑在其父类 AbstractHandlerMethodMapping 中定义。

2. 入口:afterPropertiesSet()
1
2
3
4
5
6
// RequestMappingHandlerMapping
@Override
public void afterPropertiesSet() {
// 创建配置类?实际上这里调用了父类的同名方法
super.afterPropertiesSet();
}

父类 AbstractHandlerMethodMapping 实现了此方法:

1
2
3
4
5
// AbstractHandlerMethodMapping
@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() {
// 获取容器中所有 bean 的名称
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) {
// 忽略无法获取类型的 bean
}
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);
// 使用 MethodMetadata 或反射获取所有方法,并找出有映射的方法
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 中。
5. 构建映射信息:getMappingForMethod()
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 注解
RequestMapping methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
// 先构建方法级别的条件
info = createRequestMappingInfo(methodAnnotation, method);
// 获取类级别的 @RequestMapping 注解
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) {
// 可以自定义条件(如通过 setCustomCondition 扩展)
RequestCondition<?> condition = (method != null ? getCustomMethodCondition(method) : getCustomTypeCondition(annotation.targetClass()));
// 使用建造者模式构建 RequestMappingInfo
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);
}

MappingRegistryAbstractHandlerMethodMapping 的内部类,它持有两个核心映射:

  • 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 的映射。它维护了一个 handlerMapMap<String, Object>),用于存储 URL 路径到处理器(Bean 实例或 Bean 名称)的映射,并提供了基于 PathMatcher 的路径匹配算法。
  • AbstractDetectingUrlHandlerMapping:继承自 AbstractUrlHandlerMapping,增加了自动检测容器中所有 Bean 并注册 URL 的能力。它定义了一个模板方法 detectHandlers(),子类只需实现 determineUrlsForHandler(String beanName) 方法,返回该 Bean 对应的 URL 数组。
  • BeanNameUrlHandlerMapping:最终实现类,仅需实现 determineUrlsForHandler,将 Bean 名称和别名中以 / 开头的字符串作为 URL 返回。

BeanNameUrlHandlerMapping

2. detectHandlers():扫描容器中所有 Bean

BeanNameUrlHandlerMapping 实现了 ApplicationContextAware 接口(通过父类),并通常在容器启动时由 DispatcherServletinitHandlerMappings 触发初始化。它的初始化入口位于 AbstractDetectingUrlHandlerMappinginitApplicationContext() 方法(该方法在 Spring 容器刷新时被调用)。

1
2
3
4
5
6
// AbstractDetectingUrlHandlerMapping
@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();
// 获取容器中所有 Bean 的名称(可配置是否包括祖先容器)
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));

for (String beanName : beanNames) {
// 调用子类实现的 determineUrlsForHandler 获取该 Bean 对应的 URL 数组
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// 注册这些 URL 到父类的 handlerMap 中
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<>();
// 如果 Bean 名称以 "/" 开头,则将其作为一个 URL
if (beanName.startsWith("/")) {
urls.add(beanName);
}
// 同样检查该 Bean 的所有别名
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):存入映射表

AbstractUrlHandlerMappingregisterHandler 方法将每个 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;
// 如果 handler 是 String(即 beanName),且非懒加载且为单例,则提前获取其实例
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
// 将 URL 和处理器存入 handlerMap
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 {
// 查找指定名称的 HandlerAdapter
// ...
}

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 {
// 查找指定名称的 HandlerExceptionResolver
// ...
}

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 {
// 查找指定名称的 ViewResolver
// ...
}

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组件初始化的几个核心原则:

  1. 优先使用容器中的Bean:允许开发者通过@Bean或XML配置覆盖默认组件,实现高度定制。
  2. 支持多组件共存:对于HandlerMappingHandlerAdapterViewResolver等,通过detectAll标志和排序机制,允许多个组件协同工作。
  3. 智能的默认策略:如果开发者未提供任何配置,SpringMVC会从DispatcherServlet.properties中加载一组合理的默认实现,让应用“开箱即用”。
  4. 按需加载:像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 {
// 1. 对于include请求,保存原始请求的属性快照,以便之后恢复
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));
}
}
}

// 2. 将一些重要的SpringMVC对象设置到request属性中,便于Handler或View使用
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());

// 3. 处理FlashMap(用于重定向传参)
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 {
// 4. 核心分发逻辑
doDispatch(request, response);
}
finally {
// 5. 恢复快照(如果是include请求)
// ...
}
}

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 {
// 1. 检查是否是文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// 2. 获取当前请求的处理器执行链(Handler + Interceptors)[核心步骤A]
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果没有找到处理器,通过response返回404(由NoHandlerFoundException处理)
noHandlerFound(processedRequest, response);
return;
}

// 3. 获取能够执行该处理器的适配器 [核心步骤B]
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// 4. 处理 HTTP 缓存相关(Last-Modified)
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;
}
}

// 5. 执行拦截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 如果某个拦截器的preHandle返回false,请求处理流程就此中断
return;
}

// 6. 真正执行处理器(调用Controller的方法)[核心步骤C]
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 7. 如果需要异步处理,直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

// 8. 如果处理器没有返回视图名,应用默认的视图名(如请求路径)
applyDefaultViewName(processedRequest, mv);

// 9. 执行拦截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
// 记录异常
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}

// 10. 处理执行结果(包括异常处理和视图渲染)[核心步骤D]
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;
}

逻辑解析

  • handlerMappingsDispatcherServlet 持有的 HandlerMapping 列表,在初始化阶段通过 initHandlerMappings() 加载(包括自定义和默认的 HandlerMapping)。
  • 遍历该列表,依次调用每个 HandlerMappinggetHandler(request) 方法。
  • 一旦某个 HandlerMapping 返回非空的 HandlerExecutionChain,立即将其返回,不再继续后续的 HandlerMapping
  • 如果所有 HandlerMapping 都返回 null,则整个 getHandler 返回 null,后续由 noHandlerFound 处理(通常导致 404)。

这种“短路”的设计保证了请求处理的效率,同时也允许我们通过调整 HandlerMapping 的顺序来控制不同映射策略的优先级。

4.3.2 HandlerMapping.getHandler() 的通用实现:AbstractHandlerMapping

HandlerMapping 是一个接口,其最核心的实现都继承自抽象类 AbstractHandlerMapping。该抽象类提供了获取处理器执行链的通用骨架,而将具体的处理器查找逻辑留给子类(如 RequestMappingHandlerMappingBeanNameUrlHandlerMapping)通过 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 {
// 1. 由子类实现的模板方法:获取原始的处理器对象(可能是 HandlerMethod、Controller 实例等)
Object handler = getHandlerInternal(request);
if (handler == null) {
// 如果子类没找到,尝试获取默认处理器(可配置)
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}

// 2. 如果处理器是 String 类型(表示 bean 名称),则从容器中解析出实际的 bean 实例
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}

// 3. 获取已注册的 HandlerExecutionChain(将处理器包装成链,并装配拦截器)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

// 4. 处理 CORS 跨域请求(如果存在跨域配置,可能返回一个特殊的处理器)
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) {
// 如果 handler 已经是 HandlerExecutionChain,直接使用;否则新建一个
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

// 获取请求的查找路径(用于匹配 MappedInterceptor)
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);

// 遍历所有注册的拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
// 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 包含了处理器和一个有序的拦截器列表,在后续的 preHandlepostHandleafterCompletion 阶段会依次调用。

4.3.3 子类 getHandlerInternal 的实现示例

为了更全面地理解 getHandler 的整体流程,我们选取两个典型子类,看看它们如何实现 getHandlerInternal

1. AbstractHandlerMethodMapping.getHandlerInternalRequestMappingHandlerMapping 的父类)
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.getHandlerInternalBeanNameUrlHandlerMapping 的父类)
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 请求,携带 OriginAccess-Control-Request-Method 头,询问服务器是否允许。Spring MVC 会为此类请求专门创建一个 PreFlightHandler 或通过 CorsInterceptor 处理,直接返回允许的跨域头,而不需要执行实际的处理器。
  • **getCorsHandlerExecutionChain**:如果当前请求是预检请求,或者处理器配置了跨域规则,该方法会返回一个特殊的执行链,其中可能包含 CorsInterceptor,或者在预检请求时直接返回一个处理 OPTIONS 的内置处理器。

4.5 没有找到处理器的情况

如果所有 HandlerMapping 都返回 nullDispatcherServlet.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) {
// 判断当前适配器是否支持这个handler
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");
}

逻辑解析

  1. 遍历 handlerAdapters 列表handlerAdaptersDispatcherServlet 持有的 HandlerAdapter 列表,在初始化阶段通过 initHandlerAdapters() 加载(同样支持自定义和默认的 adapter)。
  2. **调用 supports(handler)**:对列表中的每个 adapter,调用其 supports 方法,判断该 adapter 是否能处理传入的 handler 对象。
  3. 返回第一个匹配的 adapter:一旦找到支持当前 handler 的 adapter,立即返回,不再继续遍历。
  4. 若未找到,抛出异常:如果遍历完所有 adapter 都没有找到支持的,抛出 ServletException,提示配置中缺少合适的 HandlerAdapter

这个方法的逻辑简洁明了,但背后的设计思想却十分重要——适配器模式的典型应用。DispatcherServlet 不关心 handler 的具体类型(可能是 HandlerMethodController 实例、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) {
// 查找容器中所有 HandlerAdapter 类型的 Bean,包括祖先容器
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 {
// 仅查找指定名称的 HandlerAdapter
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
} catch (NoSuchBeanDefinitionException ex) {
// 忽略
}
}

// 如果仍为 null,则使用默认策略(从 DispatcherServlet.properties 加载)
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
// ...
}
}

默认情况下,detectAllHandlerAdapterstrue,因此 Spring 会收集容器中所有 HandlerAdapter 类型的 Bean,并按 @OrderOrdered 接口排序。若没有自定义任何 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 各 HandlerAdaptersupports 逻辑剖析

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-elseswitch 语句,针对每种处理器类型分别调用其特定的处理方法(如 HttpRequestHandler.handleRequest()Controller.handleRequest()HandlerMethod.invokeAndHandle())。这不仅使代码臃肿,而且难以扩展新的处理器类型。
  • 有了适配器模式,每种处理器类型都有对应的适配器,将不同风格的调用接口统一为 HandlerAdapterhandle 方法。DispatcherServlet 只需调用 adapter.handle(request, response, handler) 即可,完全无需关心 handler 的具体类型。这就是“面向接口编程”的经典实践。

4.4.5 异常情况分析

如果 getHandlerAdapter 抛出 ServletException,意味着当前 handler 的类型没有任何一个已注册的 HandlerAdapter 能够处理。可能的原因有:

  1. **自定义了新的处理器类型,却没有提供对应的 HandlerAdapter**:例如,你编写了一个实现某个自定义接口的处理器,但忘记注册对应的 adapter。
  2. **误删了默认的 HandlerAdapter**:如果你在容器中覆盖了所有默认 adapter,但新的 adapter 集合未能覆盖所有实际使用的处理器类型,就会导致某些请求失败。
  3. 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());

// 执行拦截器 preHandle、处理器、拦截器 postHandle 等...

由此可见,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
// org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
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 {
// 1. 创建方法调用的数据绑定工厂等
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

// 2. 将请求和响应包装成ServletWebRequest
ServletWebRequest webRequest = new ServletWebRequest(request, response);

// 3. 创建方法调用器
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); // 设置参数解析器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); // 设置返回值处理器
invocableMethod.setDataBinderFactory(binderFactory);

// 4. 执行并处理Model
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// ... 处理@SessionAttributes等
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
// org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod
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];
// 尝试从providedArgs中找(如@PathVariable等有时已经提前解析)
// ...

// 遍历所有注册的参数解析器
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 方法接收 requestresponse 以及之前通过 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 {

// 检查请求方法是否支持(如检查是否允许 GET、POST 等)
checkRequest(request);

ModelAndView mav;
if (this.synchronizeOnSession) {
// 如果配置了在 Session 上同步执行,则对 session 加锁
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);
}

// 如果没有返回视图,但请求方法不是 HEAD,则设置一个默认的视图名称(如果有配置)
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 {

// 将 request/response 包装为 ServletWebRequest,提供对原生请求的包装
ServletWebRequest webRequest = new ServletWebRequest(request, response);

// 创建数据绑定工厂(负责创建 WebDataBinder 实例,用于参数绑定和校验)
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

// 创建模型工厂(管理 @ModelAttribute 方法和 SessionAttributes)
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

// 创建 ServletInvocableHandlerMethod,这是真正执行方法调用的对象
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

// 设置参数解析器(从 request 中解析方法参数)
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);

// 设置返回值处理器(处理方法返回值,如 @ResponseBody、ModelAndView 等)
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);

// 设置数据绑定工厂
invocableMethod.setDataBinderFactory(binderFactory);

// 设置参数名发现器(用于获取参数名称,例如在 @RequestParam 未指定 name 时使用)
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

// 创建 ModelAndView 容器,用于在整个调用过程中持有模型和视图信息
ModelAndViewContainer mavContainer = new ModelAndViewContainer();

// 添加 @SessionAttributes 中的属性到模型(从 session 中取出放到模型)
modelFactory.initModel(webRequest, mavContainer, invocableMethod);

// 异步处理相关
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

// 执行方法调用
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);

// 根据调用结果返回 ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}

步骤分解

  • **创建 ServletWebRequest**:包装原生 request/response,方便后续访问。
  • **getDataBinderFactory**:收集当前 HandlerMethod 及其所属 Controller 上标注的 @InitBinder 方法,创建一个 WebDataBinderFactory,用于后续参数绑定。
  • **getModelFactory**:收集 @ModelAttribute 方法(用于在调用目标方法前预先准备模型属性)以及 @SessionAttributes 配置,创建 ModelFactory
  • **createInvocableHandlerMethod**:将 HandlerMethod 包装为可调用的 ServletInvocableHandlerMethod,这个类扩展了 HandlerMethod,增加了调用和参数解析能力。
  • 设置参数解析器和返回值处理器argumentResolversreturnValueHandlersRequestMappingHandlerAdapter 初始化时装配好的两个重要组件列表,分别负责将请求数据转换为方法参数,以及处理方法返回值。
  • **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);

// 设置响应状态(如果方法上有 @ResponseStatus 注解)
setResponseStatus(webRequest);

// 如果返回值为 null,检查是否有 @ResponseStatus 且 reason 非空,则可能不需要进一步处理
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
// 请求已经处理完毕(如直接写入 response),标记为请求已处理,后续无需视图渲染
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(getResponseStatusReason())) {
// 如果有 @ResponseStatus(reason=...),则抛出异常由 Spring 处理
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:处理 @ResponseBodyResponseEntity 等,直接将数据写入 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);

// 输出调试日志(如果开启了 TRACE 级别)
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);

// 优先使用 providedArgs(通常为空,除非有特殊提前解析的参数)
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 方法,将 @SessionAttributes 中需要更新的属性写回 session
modelFactory.updateModel(webRequest, mavContainer);

// 如果请求已经被处理(例如返回了 @ResponseBody),则返回 null
if (mavContainer.isRequestHandled()) {
return null;
}

// 构建 ModelAndView:模型从 mavContainer.getModel() 获取,视图从 mavContainer.getView() 或 getViewName() 获取
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}

// 如果模型中包含重定向属性,则将其存入 FlashMap
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 = (Controller) handler;

// 调用 Controller 的 handleRequest 方法
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
HttpRequestHandler handlerToUse = (HttpRequestHandler) handler;

// 调用处理器的 handleRequest 方法
handlerToUse.handleRequest(request, response);

// 直接返回 null,因为处理器已经直接写入了 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 的场景,由于已经直接写入了响应,mvnull,因此 render 方法不会执行,请求结束。

对于 SimpleControllerHandlerAdapter 返回的 ModelAndView,如果包含视图名,则会经过视图解析和渲染。

4.5.6 异常处理

所有 handle 方法都声明抛出 Exception,因此处理器执行过程中抛出的任何异常都会直接向上传递。DispatcherServletdoDispatch 中会捕获这些异常,并交给 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;

// 1. 处理异常!
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
// 如果是自定义异常,直接从中获取ModelAndView
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
// 否则,交给HandlerExceptionResolver处理
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

// 2. 如果返回的ModelAndView不为空,且没有异常,则进行渲染
if (mv != null && !mv.wasCleared()) {
render(mv, request, response); // 核心渲染方法
// ...
} else {
// ...
}

// 3. 最后,触发拦截器的afterCompletion方法
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) {
// 1. 通过ViewResolver解析视图名,得到View对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "'...");
}
} else {
// 直接使用返回的View对象
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object.");
}
}

// 2. 执行视图渲染!将Model中的数据填充到View中
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 processDispatchResultdoDispatch 中的位置

先回顾一下 doDispatch 的末尾部分:

1
2
3
4
5
6
7
8
9
10
11
12
try {
// ... 前面的步骤:检查 multipart、获取 HandlerExecutionChain、获取 HandlerAdapter、执行拦截器 preHandle、执行处理器 ha.handle、应用默认视图名、执行拦截器 postHandle
}
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;

// 1. 处理异常(如果存在)
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
// 如果异常本身已经定义了 ModelAndView(如某些自定义异常),则直接使用它
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
// 否则,使用 HandlerExceptionResolver 解析异常,得到 ModelAndView
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

// 2. 检查 ModelAndView 是否需要渲染
if (mv != null && !mv.wasCleared()) {
// 渲染视图
render(mv, request, response);
if (errorView) {
// 如果是错误视图,在渲染后清除请求中的错误属性(避免重复记录)
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
// 如果 ModelAndView 为 null 或已被清除,可能处理器已经直接处理了响应(如返回 @ResponseBody)
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}

// 3. 触发拦截器的 afterCompletion 方法(整个请求处理完成后的回调)
if (mappedHandler != null) {
// 注意:这里传入的异常是 dispatchException,即处理器执行过程中抛出的异常
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;

// 遍历所有注册的 HandlerExceptionResolver
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}

if (exMv != null) {
// 如果解析器返回了 ModelAndView,但可能没有设置视图名或视图对象
if (exMv.isEmpty()) {
// 设置默认的视图名称(通常是请求路径)
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isTraceEnabled()) {
logger.trace("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
// 将异常信息暴露给请求属性,以便错误视图可以访问(如使用 <%@ page isErrorPage="true" %>)
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}

// 如果没有 HandlerExceptionResolver 处理该异常,则重新抛出
throw ex;
}

关键点

  • handlerExceptionResolvers 列表包含所有实现了 HandlerExceptionResolver 的 Bean,如 ExceptionHandlerExceptionResolver(处理 @ExceptionHandler)、ResponseStatusExceptionResolver(处理 @ResponseStatus)、DefaultHandlerExceptionResolver(处理 Spring 内部异常)。
  • 解析器一旦返回非空的 ModelAndView,即停止遍历。返回的 ModelAndView 可能为空(仅有模型,需要后续确定视图名),此时会调用 getDefaultViewName 设置默认视图名(通常基于请求路径)。
  • 调用 WebUtils.exposeErrorRequestAttributes 将异常信息放入 request 属性(如 javax.servlet.error.exceptionjavax.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 locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);

View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 1. 通过 ViewResolver 解析视图名,得到 View 对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "'...");
}
} else {
// 2. 如果 ModelAndView 中已经包含了 View 对象,则直接使用
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object.");
}
}

// 3. 视图渲染:将模型数据填充到视图中,并生成响应
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 属性)。

注意:如果 mvwasCleared() 返回 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)的三个方法分别在请求生命周期的不同阶段被调用:

  1. preHandle:在HandlerAdapter执行处理器之前执行。如果返回falseDispatcherServlet将不再执行后续的处理器和postHandle,但会执行已执行过的拦截器的afterCompletion(逆序)。
  2. postHandle:在处理器执行之后、processDispatchResult渲染视图之前执行。此时可以修改ModelAndView
  3. afterCompletion:在processDispatchResult执行完毕、整个请求处理完成之后执行。通常用于资源清理。

这种设计为我们在不侵入业务代码的前提下,提供了强大的AOP式横切关注点(如日志、权限、性能监控)的支持。

六、总结

DispatcherServlet从一个init参数开始,逐步构建起一个庞大的九大组件生态;看到了一个HTTP请求如何被一步步拆解、匹配、适配、反射调用,最终渲染成响应;参数解析器如何将网络数据和对象模型无缝转换;也看到了拦截器如何在关键节点上织入横切逻辑。

​ 理解这些源码,不仅能帮助我们解决线上疑难杂症(如参数绑定失败、拦截器不生效),更能让我们在未来的架构设计中,借鉴这种高内聚、低耦合、面向接口、关注点分离的哲学。


SpringMVC全流程源码解析
https://johnjoyjzw.github.io/2023/03/01/SpringMVC源码解析/
Author
JiangZW
Posted on
March 1, 2023
Licensed under