Spring源码-SpringMVC-HandlerMapping

总结摘要
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;
}

使用场景

流程

  1. 当请求到达DispatcherServlet时,它会询问所有已注册的HandlerMapping
  2. 每个HandlerMapping检查自己是否能处理该请求
  3. 找到匹配的HandlerMapping后,返回一个HandlerExecutionChain(包含处理器和拦截器)
  4. 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

具体流程

  1. org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
  2. org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
  3. 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
    -> 打日志

分支 RequestMappingHandlerMapping#getMappingForMethod

 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 信息封装为 MappingRegistrationorg.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