前言
如果只知道SpringBoot
的核心注解是@SpringBootApplication
,未免显得只知道八股文,而缺乏对SpringBoot
源码的认识。
此处以一个数据中台项目中查询接口动态注册、注销使用的RequestMappingHandlerMapping
类,展开进行分析。
分析
在数据中台项目中,动态注册、注销请求用到了SpringMVC
提供的RequestMappingHandlerMapping
类,这个类的源码我阅读过。
核心流程图:
css
graph TD
A[容器启动] --> B[afterPropertiesSet]
B --> C[扫描所有@Controller Bean]
C --> D[解析类和方法上的@RequestMapping]
D --> E[生成RequestMappingInfo]
E --> F[注册到MappingRegistry]
F --> G[等待请求]
G --> H[接收HTTP请求]
H --> I[解析请求路径和参数]
I --> J[查找匹配的RequestMappingInfo]
J --> K[选择最佳匹配]
K --> L[返回HandlerMethod]
在Spring MVC
框架中,RequestMappingHandlerMapping
是处理 @RequestMapping
注解的核心类,负责将HTTP
请求映射到对应的控制器方法。以下是对其源码的详细分析,从设计结构、核心方法到关键流程逐步展开:
- 类继承与职责:
RequestMappingHandlerMapping
继承自AbstractHandlerMethodMapping<RequestMappingInfo>
,而后者实现了 HandlerMapping
接口,主要职责包括:
- 扫描并注册 :在应用启动时扫描所有控制器
Bean
,解析类和方法上的@RequestMapping
注解,生成映射关系。 - 请求匹配 :根据
HTTP
请求的URL
、方法、头等信息,找到最匹配的处理器方法(HandlerMethod
)。 - 路径处理 :支持
Ant
风格路径、路径变量、内容协商等特性。
关键父类方法:
registerHandlerMethod()
:将HandlerMethod
与其对应的RequestMappingInfo
注册到内部映射表。getMappingForMethod()
:在子类中实现,用于生成方法级别的RequestMappingInfo
- 初始化与Bean扫描
入口方法:afterPropertiesSet()
,在Spring
容器初始化时,该方法被调用,触发控制器方法的扫描与注册。
typescript
@Override
public void afterPropertiesSet() {
// 配置跨域相关(CORS)
super.afterPropertiesSet();
// 初始化路径匹配工具(如PathPatternParser或AntPathMatcher)
if (this.mappingRegistry == null) {
this.mappingRegistry = new MappingRegistry();
}
}
关键流程:
- 扫描所有Bean :遍历容器中的
Bean
,检查是否带有@Controller
或@RequestMapping
注解。 - 解析控制器方法 :对每个符合条件的
Bean
,解析其所有方法,生成RequestMappingInfo
。 - 注册映射关系 :将
RequestMappingInfo
与HandlerMethod
存入MappingRegistry
。
- 生成RequestMappingInfo
核心方法:getMappingForMethod()
,该方法解析方法和类上的@RequestMapping
注解,合并生成最终的RequestMappingInfo
。
ini
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 1. 解析方法上的@RequestMapping
RequestMappingInfo methodInfo = createRequestMappingInfo(method);
if (methodInfo == null) {
return null;
}
// 2. 解析类上的@RequestMapping(作为前缀)
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 合并类和方法级别的配置(路径、请求方法等)
methodInfo = typeInfo.combine(methodInfo);
}
return methodInfo;
}
注解合并规则:
- 路径拼接 :类路径 + 方法路径(如
/api
+/users
→/api/users
)。 - 请求方法:若类和方法均指定,以方法级别为准。
- 其他属性 (如
params
,headers
):合并为逻辑"与"条件。
- 注册映射到MappingRegistry
核心类:MappingRegistry
(内部类),存储所有注册的映射关系,支持快速查找。
arduino
class MappingRegistry {
// 映射表:Key=RequestMappingInfo, Value=MappingRegistration
private final Map<RequestMappingInfo, MappingRegistration> registry = new HashMap<>();
// URL路径直接映射表(优化快速匹配)
private final MultiValueMap<String, RequestMappingInfo> pathLookup = new LinkedMultiValueMap<>();
public void register(RequestMappingInfo info, HandlerMethod handlerMethod) {
// 1. 去重检查
if (this.registry.containsKey(info)) {
throw new IllegalStateException("重复的映射: " + info);
}
// 2. 注册到映射表
this.registry.put(info, new MappingRegistration(info, handlerMethod));
// 3. 按URL路径分类存储(用于快速匹配)
for (String path : info.getDirectPaths()) {
this.pathLookup.add(path, info);
}
}
}
- 请求匹配流程
入口方法:getHandlerInternal()
,当请求到达时,DispatcherServlet
调用此方法查找匹配的 HandlerMethod
。
java
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 1. 获取请求路径(解析并解码)
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 2. 查找所有匹配的RequestMappingInfo
List<RequestMappingInfo> matches = new ArrayList<>();
addMatchingMappings(this.mappingRegistry.getMappingsByUrl(lookupPath), request, matches);
// 3. 排序并选择最佳匹配
if (!matches.isEmpty()) {
RequestMappingInfo bestMatch = selectBestMatch(matches, request);
// 4. 返回对应的HandlerMethod
return this.mappingRegistry.getHandlerMethod(bestMatch);
}
return null;
}
匹配规则:
- 路径匹配:优先匹配直接路径,支持通配符和路径变量。
- 条件匹配:检查请求方法、参数、头部等。
- 排序规则:更具体的路径(如无通配符)优先级更高。
- 路径匹配策略
- PathPatternParser (默认):
Spring 5.3+
引入,基于预解析的路径模式,性能更优。 - AntPathMatcher :传统
Ant
风格路径匹配,可通过配置切换。
typescript
// 配置PathPatternParser(Spring Boot配置示例)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(new PathPatternParser());
}
}
- 跨域处理(CORS)
通过 @CrossOrigin
注解或全局配置,RequestMappingHandlerMapping
会生成对应的 CorsConfiguration
,并在匹配时应用。
scss
// 在注册映射时处理CORS配置
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
// 解析@CrossOrigin注解
CorsConfiguration corsConfig = getCorsConfiguration(handler, method, mapping);
super.registerHandlerMethod(handler, method, mapping, corsConfig);
}
- 异步请求支持
对于返回 DeferredResult
、Callable
等方法,RequestMappingHandlerMapping
会生成对应的 HandlerMethod
,后续由 RequestMappingHandlerAdapter
处理异步执行。