总结摘要
SpringMVC HandlerMapping Introduction
HandlerMapping 介绍
简介
HandlerMapping
是一个接口,它定义了如何将传入的 HTTP 请求与对应的处理器(Controller 或其他处理逻辑)进行匹配和映射。它是 Spring MVC 请求处理流程中的一个关键环节,位于请求分发器(DispatcherServlet)和处理器(Handler)之间。
类定义
1
2
3
4
| // org.springframework.web.servlet.HandlerMapping
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
|
使用场景
流程
- 当请求到达DispatcherServlet时,它会询问所有已注册的HandlerMapping
- 每个HandlerMapping检查自己是否能处理该请求
- 找到匹配的HandlerMapping后,返回一个HandlerExecutionChain(包含处理器和拦截器)
- DispatcherServlet使用返回的处理器处理请求
代码
1
2
3
4
| -> org.springframework.web.servlet.DispatcherServlet#doDispatch
-> org.springframework.web.servlet.DispatcherServlet#getHandler
-> for-each org.springframework.web.servlet.DispatcherServlet#handlerMappings
-> org.springframework.web.servlet.HandlerMapping#getHandler
|
常见实现类
实现类 | 功能描述 | 适用场景 | 典型配置 |
---|
RequestMappingHandlerMapping | 基于注解的请求映射(如 @RequestMapping 、@GetMapping 等)。 | 处理基于注解的控制器方法映射(最常用)。 | 默认启用,无需额外配置。 |
SimpleUrlHandlerMapping | 基于简单的 URL 路径映射到处理器 Bean。 | 静态资源处理或简单请求映射。 | 需手动配置 URL 到处理器 Bean 的映射关系。 |
BeanNameUrlHandlerMapping | 将请求 URL 映射到与 URL 名称匹配的处理器 Bean。 | 处理器 Bean 名称直接与 URL 对应的场景。 | 默认启用,处理器 Bean 名称需与 URL 一致。 |
ControllerClassNameHandlerMapping | 将请求 URL 映射到控制器类名(如 HomeController 映射到 /home )。 | 基于类名约定的请求处理。 | 需手动配置,通常用于特定开发约定。 |
RouterFunctionMapping | 处理基于函数式编程风格的路由定义。 | 使用 Spring WebFlux 或函数式路由定义的场景。 | 需定义 RouterFunction ,通常用于 WebFlux 应用。 |
SimpleServletHandlerAdapter | 处理标准的 Servlet。 | | |
RequestMappingHandlerMapping
简介
RequestMappingHandlerMapping
是最常用的一种,支持通过 @RequestMapping
注解来定义请求与处理器方法的映射关系。它可以根据 URL、HTTP 方法、请求参数等多种条件进行精确匹配。
类定义
1
2
3
4
5
6
7
| // Creates RequestMappingInfo instances from type and method-level @RequestMapping annotations in @Controller classes.
// org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {}
// instanceof MatchableHandlerMapping, HandlerMapping, InitializingBean
// instanceof WebApplicationObjectSupport, ApplicationObjectSupport
|
自动配置机制
SpringBoot autoconfigure 依赖包自动注册 bean RequestMappingHandlerMapping。
配置信息 spring-boot-autoconfigure-2.7.18.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
具体流程
- org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
- org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
- org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping
1
2
3
4
5
6
7
8
| // org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
}
|
初始化流程
入口
RequestMappingHandlerMapping bean 创建过程中回调扩展点方法 InitializingBean#afterPropertiesSet。
1
2
3
4
| org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
-> 加载依赖对象,加载控制参数
-> org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
-> org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
|
主流程
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.handler.AbstractHandlerMethodMapping#initHandlerMethods
-> org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getCandidateBeanNames
-> 加载所有 Object.class 类型的 bean name,调用 `getBeanNamesForType(Object.class)`,参数 this.detectHandlerMethodsInAncestorContexts 控制是否从父容器中加载。
-> for-each beanNames
-> org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean
-> 获取 bean 的类型,调用 `getType(beanName)`。
-> 筛选 handler method,筛选条件是作用于 class 的注解 Controller.class 或 RequestMapping.class。
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler
-> 解析 handler method。调用 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods
-> 重新根据 beanName 获取 bean 的类型。
-> 在 CGLIB 创建子类的场景下,获取原始的用户定义的类型,调用 org.springframework.util.ClassUtils#getUserClass(java.lang.Class<?>)
-> for-each methods
-> 详情参见分支。org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod。
-> for-each methods
-> org.springframework.aop.support.AopUtils#selectInvocableMethod
-> 目前未发现实际作用,详细作用未知。
-> 此处 handlerType 始终是未被代理的 class 类型,method是该类的方法,应当无需调用该方法。
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#registerHandlerMethod
-> org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod
-> 详情参见分支。this.mappingRegistry.register(mapping, handler, method);
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#updateConsumesCondition
-> for-each parameters,如果存在注解 RequestBody.class,则配置属性 org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition#bodyRequired,调用 org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition#setBodyRequired。
-> org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#handlerMethodsInitialized
-> 打日志
|
1
2
3
4
5
6
7
8
9
10
| org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(java.lang.reflect.AnnotatedElement)
-> 解析 method 上的注解 RequestMapping.class。
-> 封装为 RequestMappingInfo,调用 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(org.springframework.web.bind.annotation.RequestMapping, org.springframework.web.servlet.mvc.condition.RequestCondition<?>)
-> 解析 class 上的注解 RequestMapping.class,封装为 RequestMappingInfo。调用方法同解析 method 上的注解。
-> 将 method 注解封装的 RequestMappingInfo 与 class 的封装结果合并。
-> pathPrefixes 相关处理。
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#pathPrefixes
-> RequestMappingInfo.paths(prefix).options(this.config).build().combine(info)
-> 相关使用场景。org.springframework.web.servlet.config.annotation.PathMatchConfigurer#addPathPrefix
|
分支 MappingRegistry#register
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register
-> 并发控制。org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#readWriteLock
-> 将 method 封装为 HandlerMethod,调用 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#createHandlerMethod。
-> org.springframework.web.method.HandlerMethod#HandlerMethod
-> 初始化入参,调用 org.springframework.web.method.HandlerMethod#initMethodParameters
-> 解析方法与类上的注解 ResponseStatus.class,其中的 reason 信息支持国际化,调用 org.springframework.web.method.HandlerMethod#evaluateResponseStatus。
-> 校验方法是否存在重复注册,调用 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#validateMethodMapping。
-> 获取 directPaths,调用 AbstractHandlerMethodMapping.this.getDirectPaths(mapping)。
-> for-each directPaths
-> 追加到 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#pathLookup 中。变量定义为 private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();。
-> 为 HandlerMethod 创建名称,调用 org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy#getName。
-> 将名称缓存到 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#nameLookup。
-> 调用 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#addMappingName。
-> 变量定义为 private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
-> 根据注解 CrossOrigin.class 配置 cors 属性,调用 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#initCorsConfiguration。、
-> 将 cors 配置缓存在 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#corsLookup,变量定义为 private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();。
-> 将 URL 信息封装为 MappingRegistration。org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistration#MappingRegistration
-> 将 URL 信息缓存在 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#registry。
-> 变量定义为 private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
-> 代码是 this.registry.put(mapping, new MappingRegistration<>()),此处 mapping 的类型是 RequestMappingInfo,来自于方法 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getMappingForMethod。
|
END