0%

HandlerMapping

Spring MVC HandlerMapping 详解:处理器映射器的原理与实战

HandlerMapping 是 Spring MVC 中连接 “HTTP 请求” 与 “处理器(Handler,即 Controller)” 的核心组件,其核心职责是 根据请求的 URL 或其他属性,找到对应的 Handler 及拦截器(Interceptor),并封装为 HandlerExecutionChain 对象返回给 DispatcherServlet。从 “核心定义→实现类对比→配置方式→源码解析” 四个维度,彻底讲透 HandlerMapping 的工作机制。

HandlerMapping 核心定义与作用

HandlerMapping 是一个接口,定义了所有处理器映射器的核心行为,其本质是 “请求→Handler” 的映射器,相当于 MVC 中的 “路由表”。

1. 核心接口方法

1
2
3
4
5
6
7
8
public interface HandlerMapping {
/**
* 根据 HTTP 请求,查找对应的 Handler 及拦截器,封装为 HandlerExecutionChain
* @param request 当前 HTTP 请求
* @return HandlerExecutionChain(包含 Handler + 拦截器),无匹配则返回 null
*/
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
  • 返回值 HandlerExecutionChain:包含两部分核心内容:
    1. Handler:具体的业务处理器(如 @Controller 类的方法,封装为 HandlerMethod);
    2. HandlerInterceptor 数组:与当前请求匹配的拦截器,按配置顺序存储。

2. 核心数据结构:handlerMap

几乎所有 HandlerMapping 实现类内部都会维护一个 Map<String, Object> 类型的 handlerMap,用于存储 “URL 路径→Handler” 的映射关系,确保请求到来时能快速查找。例如:

1
2
// 以 AbstractUrlHandlerMapping 为例,维护 URL 到 Handler 的映射
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
  • Key:URL 路径(如 /user/list/api/login);
  • Value:Handler 对象(如 UserController 实例、HandlerMethod 实例)。

HandlerMapping 主要实现类与适用场景

HanderMapping继承关系

Spring MVC 提供了多种 HandlerMapping 实现,支持不同的映射策略(如按 Bean 名、按类名、按注解),以下是常用实现类的对比:

实现类 核心映射逻辑 适用场景 优势 缺点
BeanNameUrlHandlerMapping 将 Controller Bean 的名称(需以 / 开头)作为 URL 路径 简单项目、XML 配置 Controller(如实现 Controller 接口的类) 配置简单,无额外依赖 不支持注解,URL 与 Bean 名强耦合
ControllerClassNameHandlerMapping 将 Controller 类名转为 URL(如 UserController/user 快速开发,无需手动配置 URL 零配置,类名即 URL URL 灵活性差,无法自定义路径
SimplerUrlHandlerMapping 通过 XML/Java 配置显式指定 “URL→Handler” 映射 需要精确控制 URL 与 Handler 映射关系的场景 映射关系清晰,易于维护 配置繁琐,不支持注解
DefaultAnnotationHandlerMapping 解析 @RequestMapping 注解,匹配 URL 与 Controller 类 Spring MVC 3.1 之前的注解驱动项目 支持注解,简化配置 仅支持类级别注解,不支持方法级别精确映射
RequestMappingHandlerMapping 解析类 + 方法级别的 @RequestMapping 注解,返回 HandlerMethod Spring MVC 3.1+ 注解驱动项目(主流) 支持精确路径、请求方法、参数等匹配 依赖注解配置,无注解则无法使用

关键区别:DefaultAnnotationHandlerMapping vs RequestMappingHandlerMapping

  • DefaultAnnotationHandlerMapping(过时):
    • 仅能解析类级别的 @RequestMapping,方法级别的映射需依赖 AnnotationMethodHandlerAdapter
    • 返回的 Handler 是整个 Controller 实例,而非具体方法。
  • RequestMappingHandlerMapping(推荐):
    • 整合了 “类级别 + 方法级别” 的 @RequestMapping 解析,直接返回封装了具体方法的 HandlerMethod
    • 支持更丰富的匹配规则(如请求方法 method = RequestMethod.GET、请求参数 params = "id");
    • mvc:annotation-driven 自动注册的默认 HandlerMapping。

HandlerMapping 配置方式

HandlerMapping 的配置分为 “手动配置” 和 “自动配置” 两种,现代项目推荐使用自动配置(mvc:annotation-driven)。

1. 自动配置:mvc:annotation-driven(推荐)

mvc:annotation-driven 是 Spring MVC 3.1+ 提供的简化配置,会自动注册 RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver 三个核心 Bean,无需手动配置 HandlerMapping:

1
2
<!-- springmvc.xml 中配置 -->
<mvc:annotation-driven/>
mvc:annotation-driven 的核心作用:
  1. 自动注册核心组件:
    • RequestMappingHandlerMapping:注解驱动的 HandlerMapping;
    • RequestMappingHandlerAdapter:适配 HandlerMethod 的处理器适配器;
    • ExceptionHandlerExceptionResolver:处理 @ExceptionHandler 注解的异常解析器。
  2. 支持类型转换与格式化:
    • 注册 ConversionService,支持自定义类型转换器;
    • 支持 @NumberFormat(数字格式化)、@DateTimeFormat(日期格式化)注解。
  3. 支持数据校验:
    • 集成 JSR-380 校验(如 @Valid 注解),需依赖 hibernate-validator
  4. 支持 JSON 交互:
    • 自动注册 MappingJackson2HttpMessageConverter(需 jackson-databind 依赖),支持 @RequestBody/@ResponseBody

2. 手动配置(适用于特殊场景)

若需自定义 HandlerMapping(如 SimplerUrlHandlerMapping),可手动在 XML 或 Java 配置中声明:

示例:手动配置 SimplerUrlHandlerMapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 1. 定义 Controller Bean -->
<bean id="userController" class="com.example.controller.UserController"/>

<!-- 2. 配置 SimplerUrlHandlerMapping,指定 URL→Handler 映射 -->
<bean class="org.springframework.web.servlet.handler.SimplerUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/user/list">userController</prop> <!-- /user/list → userController -->
<prop key="/user/detail">userController</prop> <!-- /user/detail → userController -->
</props>
</property>
<!-- 优先级:值越小,优先级越高(若多个 HandlerMapping 匹配,优先使用此映射) -->
<property name="order" value="1"/>
</bean>

核心实现类源码解析:RequestMappingHandlerMapping

RequestMappingHandlerMapping 是当前注解驱动项目的默认 HandlerMapping,负责解析 @RequestMapping 注解,构建 “URL→HandlerMethod” 的映射,并处理拦截器整合。解析其核心流程。

1. 初始化流程:扫描并构建映射关系

RequestMappingHandlerMapping 实现了 ApplicationContextAwareInitializingBean 接口,在 Spring 容器初始化时完成映射关系的构建,核心步骤:

步骤 1:setApplicationContext → 初始化上下文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 继承链:RequestMappingHandlerMapping → AbstractHandlerMapping → ApplicationObjectSupport
// ApplicationObjectSupport#setApplicationContext 触发初始化
@Override
public final void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
super.setApplicationContext(applicationContext);
// 调用 AbstractHandlerMapping#initApplicationContext
initApplicationContext();
}

// AbstractHandlerMapping#initApplicationContext
protected void initApplicationContext() throws BeansException {
// 1. 扩展拦截器(空实现,供子类扩展)
extendInterceptors(this.interceptors);
// 2. 扫描容器中所有 MappedInterceptor 类型的 Bean,添加到拦截器列表
detectMappedInterceptors(this.adaptedInterceptors);
// 3. 初始化拦截器(将 interceptors 转为 HandlerInterceptor)
initInterceptors();
}
步骤 2:afterPropertiesSet → 扫描 Handler 并构建映射

InitializingBean 接口的 afterPropertiesSet 方法触发 initHandlerMethods,扫描所有带 @Controller@RequestMapping 的 Bean,解析注解并构建映射:

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
// AbstractHandlerMethodMapping#afterPropertiesSet(RequestMappingHandlerMapping 的父类)
@Override
public void afterPropertiesSet() {
// 核心:初始化 HandlerMethods(扫描并构建映射)
initHandlerMethods();
}

// AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() {
// 1. 获取容器中所有 Bean 的名称(支持扫描父容器)
String[] beanNames = detectHandlerMethodsInAncestorContexts
? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class)
: getApplicationContext().getBeanNamesForType(Object.class);

for (String beanName : beanNames) {
// 跳过作用域代理 Bean(如 @Scope("request"))
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
// 2. 获取 Bean 的类型
beanType = getApplicationContext().getType(beanName);
} catch (Throwable ex) {
// 忽略无法加载类型的 Bean
}

// 3. 判断是否为 Handler:类上有 @Controller 或 @RequestMapping 注解
if (beanType != null && isHandler(beanType)) {
// 4. 解析当前 Bean 的 HandlerMethod(类+方法级别的 @RequestMapping)
detectHandlerMethods(beanName);
}
}
}

// 5. 映射构建完成后的回调(空实现,供子类扩展)
handlerMethodsInitialized(getHandlerMethods());
}
关键方法 isHandler:判断是否为 Handler
1
2
3
4
5
6
7
// RequestMappingHandlerMapping#isHandler
@Override
protected boolean isHandler(Class<?> beanType) {
// 类上有 @Controller 或 @RequestMapping 注解,即为 Handler
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class)
|| AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
关键方法 detectHandlerMethods:解析方法级映射
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
// AbstractHandlerMethodMapping#detectHandlerMethods
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String)
? getApplicationContext().getType((String) handler)
: handler.getClass();

// 为 Handler 创建一个“方法查找器”,解析方法上的 @RequestMapping
MethodMappingPredicate predicate = method -> {
try {
// 解析方法上的 @RequestMapping,返回映射信息
return getMappingForMethod(method, handlerType) != null;
} catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect method [" + method + "]", ex);
}
};

// 1. 查找所有带 @RequestMapping 的方法
Map<Method, T> methodMappings = MethodIntrospector.selectMethods(handlerType, predicate);

// 2. 为每个方法注册映射(URL→HandlerMethod)
for (Map.Entry<Method, T> entry : methodMappings.entrySet()) {
Method method = entry.getKey();
T mapping = entry.getValue();
// 注册映射:将 URL 与 HandlerMethod 关联
registerHandlerMethod(handler, method, mapping);
}
}

2. 请求处理流程:查找 Handler 并封装执行链

当 HTTP 请求到达时,DispatcherServlet 调用 getHandler(request) 方法,RequestMappingHandlerMapping 按以下步骤查找 Handler 并封装 HandlerExecutionChain

步骤 1: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
32
// AbstractHandlerMapping#getHandler(所有 HandlerMapping 的父类实现)
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 1. 查找匹配的 Handler(核心:URL→HandlerMethod)
Object handler = getHandlerInternal(request);
// 2. 若未找到,使用默认 Handler(可配置)
if (handler == null) {
handler = getDefaultHandler();
}
// 3. 若仍未找到,返回 null(DispatcherServlet 会返回 404)
if (handler == null) {
return null;
}

// 4. 若 Handler 是字符串(如 Bean 名),从容器中获取实际 Bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}

// 5. 封装 HandlerExecutionChain(添加匹配的拦截器)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

// 6. 跨域请求处理(若为 CORS 请求,添加 CORS 拦截器)
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null) ? globalConfig.combine(handlerConfig) : handlerConfig;
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}

return executionChain;
}
步骤 2:getHandlerInternal → 查找 HandlerMethod
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
// AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 1. 获取请求的 lookupPath(标准化 URL 路径,如 /user/list)
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);

// 2. 加读锁,确保线程安全(映射关系构建时加写锁)
this.mappingRegistry.acquireReadLock();
try {
// 3. 根据 lookupPath 查找对应的 HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// 4. 解析 HandlerMethod 中的 Bean(若为代理,获取目标 Bean)
return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
} finally {
// 5. 释放读锁
this.mappingRegistry.releaseReadLock();
}
}

// 核心:根据 lookupPath 查找 HandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 1. 查找精确匹配的映射
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, lookupPath, request);
}
// 2. 若无精确匹配,查找模式匹配(如 /user/*)
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, lookupPath, request);
}

// 3. 选择最优匹配(如精确匹配优先于模式匹配)
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
// 4. 若有多个匹配,根据请求方法、参数等进一步筛选
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
}
// 5. 返回最优匹配的 HandlerMethod
return bestMatch.handlerMethod;
} else {
// 无匹配,返回 null
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
步骤 3:getHandlerExecutionChain → 添加拦截器
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
// AbstractHandlerMapping#getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 1. 创建 HandlerExecutionChain(若 handler 已为链,直接使用;否则新建)
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain)
? (HandlerExecutionChain) handler
: new HandlerExecutionChain(handler);

// 2. 获取请求的 lookupPath
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);

// 3. 遍历所有拦截器,添加与 lookupPath 匹配的拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
// 拦截器的路径匹配(如 /user/** 匹配 /user/list)
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} else {
// 非 MappedInterceptor,直接添加(全局拦截器)
chain.addInterceptor(interceptor);
}
}

return chain;
}

HandlerMapping 工作流程总结

HandlerMapping 的生命周期分为 “初始化” 和 “请求处理” 两个阶段,核心流程如下:

1. 初始化阶段(Spring 容器启动时)

  1. 扫描 HandlerRequestMappingHandlerMapping 扫描容器中所有带 @Controller@RequestMapping 的 Bean;
  2. 解析注解:解析类和方法上的 @RequestMapping 注解,提取 URL 路径、请求方法、参数等映射信息;
  3. 构建映射表:将 “URL 路径→HandlerMethod” 的映射关系存入 handlerMap,并加锁确保线程安全;
  4. 初始化拦截器:扫描容器中的拦截器,分类存入 adaptedInterceptors,供后续请求匹配。

2. 请求处理阶段(HTTP 请求到来时)

  1. 获取 lookupPath:标准化请求 URL,得到 lookupPath(如 /user/list);
  2. 查找 HandlerMethod:根据 lookupPath 从 handlerMap 中查找匹配的 HandlerMethod,优先精确匹配,再模式匹配;
  3. 封装执行链:创建 HandlerExecutionChain,添加与 lookupPath 匹配的拦截器;
  4. 跨域处理:若为 CORS 请求,添加 CORS 拦截器,处理跨域逻辑;
  5. 返回执行链:将 HandlerExecutionChain 返回给 DispatcherServlet,由后者继续调用 Handler 和拦截器。

常见问题与最佳实践

1. 多个 HandlerMapping 匹配同一请求,如何确定优先级?

  • 优先级规则:通过 order 属性控制,值越小,优先级越高(Ordered 接口);

  • 默认优先级mvc:annotation-driven 注册的 RequestMappingHandlerMapping 优先级高于手动配置的其他 HandlerMapping;

  • 解决方案:手动配置order属性,确保关键映射器优先执行:

    1
    2
    3
    4
    <bean class="org.springframework.web.servlet.handler.SimplerUrlHandlerMapping">
    <property name="order" value="0"/> <!-- 最高优先级 -->
    <property name="mappings">...</property>
    </bean>

2. 如何自定义 HandlerMapping?

若需扩展映射逻辑(如基于请求头、IP 地址匹配),可继承 AbstractHandlerMappingAbstractHandlerMethodMapping,重写以下方法:

  • getHandlerInternal:自定义 Handler 查找逻辑;
  • isHandler:自定义 Handler 判断规则;
  • getMappingForMethod:自定义方法级映射解析逻辑。
示例:基于 IP 地址的自定义 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class IpBasedHandlerMapping extends AbstractHandlerMethodMapping<IpMapping> {
// 存储 IP 段→HandlerMethod 的映射
private Map<String, HandlerMethod> ipHandlerMap = new HashMap<>();

// 注册 IP 映射
public void registerIpMapping(String ipPattern, HandlerMethod handlerMethod) {
ipHandlerMap.put(ipPattern, handlerMethod);
}

// 重写:判断是否为 Handler(复用 RequestMappingHandlerMapping 的逻辑)
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
}

// 重写:根据请求 IP 查找 HandlerMethod
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String clientIp = getClientIp(request); // 获取客户端 IP
// 根据 IP 匹配 HandlerMethod
for (Map.Entry<String, HandlerMethod> entry : ipHandlerMap.entrySet()) {
if (matchesIp(clientIp, entry.getKey())) {
return entry.getValue();
}
}
return super.getHandlerInternal(request);
}

// IP 匹配逻辑(简化:支持 * 通配符)
private boolean matchesIp(String clientIp, String ipPattern) {
return clientIp.matches(ipPattern.replace("*", ".*"));
}

// 获取客户端 IP
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
return ip == null ? request.getRemoteAddr() : ip;
}

// 其他抽象方法实现(省略)
@Override
protected IpMapping getMappingForMethod(Method method, Class<?> handlerType) { return null; }
@Override
protected void handleMatch(IpMapping mapping, String lookupPath, HttpServletRequest request) {}
@Override
protected HandlerMethod handleNoMatch(Set<IpMapping> mappings, String lookupPath, HttpServletRequest request) { return null; }
}

3. 最佳实践建议

  1. 优先使用 mvc:annotation-driven:自动注册 RequestMappingHandlerMapping,无需手动配置,支持注解驱动开发;
  2. 避免多个 HandlerMapping 冲突:若手动配置多个 HandlerMapping,通过 order 明确优先级,避免同一 URL 被多次匹配;
  3. 合理设计 URL 路径:使用 RESTful 风格 URL(如 /user/{id}),配合 @PathVariable,减少映射配置;
  4. 使用拦截器而非自定义 HandlerMapping:大部分场景下,拦截器(HandlerInterceptor)可实现请求过滤逻辑,无需自定义 HandlerMapping,降低复杂度。

总结

HandlerMapping 是 Spring MVC 路由机制的核心,通过 “初始化时构建映射表,请求时快速查找” 的模式,实现了请求与 Handler 的解耦。RequestMappingHandlerMapping 作为当前主流实现,通过解析 @RequestMapping 注解,支持精确的路径、方法、参数匹配,是注解驱动项目的首选

欢迎关注我的其它发布渠道