前言
如果只知道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 处理异步执行。