起因

新服务在处理不存在的url请求时,上报日志报错,具体错误信息:

根据报错信息找到相关代码

代码处做了对参数hanler的强制转换,但是真正传入的参数类型为 ResourceHttpRequestHandler 导致转换失败而报错,梳理一下,handler是在 自定义的日志Interceptor 的afterCompletion方法中由框架传入的

ResourceHttpRequestHandler 是什么?

查阅类文档,ResourceHttpRequestHandler是SpringMVC提供的处理静态资源的Handler,比如常见的js或者css文件。在当前前后端分离的分工下,对于REST服务开发,不会再使用这种方式提供对静态资源的访问,为什么请求了一个不存在的url,会使用静态资源的Hanlder来处理?

SpringMVC提供了哪些HandlerMapping?

SpringMVC提供的HanlderMapping位于 org.springframework.web.servlet.handler 包下,主要包含

Mapping Desc
RequestMappingHandlerMapping 处理@RequestMapping 注解映射关系
SimpleUrlHandlerMapping 使用URL模式匹配映射关系
BeanNameUrlHandlerMapping 基于Bean名匹配映射关系

SpringMVC 路径映射流程回顾

回顾一下SpringMVC的路径映射流程,通常的url请求的编码方式为:

1
2
3
4
@RequestMapping("/ping")
public Response<String> ping() {
    return Response.success("pong");
}

标识了 @RequestMapping 注解的方法会被 SpringMVC 框架的 RequestMappingHandlerMapping 类在启动时进行扫描并根据映射信息来创建 RequestMappingInfo(RequestMappingInfo 类维护请求的映射信息),扫描后的所有Mapping信息在 MappingRegistry(AbstractHandlerMethodMapping内部类) 类进行维护

SpringMVC如何处理request与HandlerMethod的映射关系

AbstractHandlerMethodMapping的getHandler 方法用来通过给定的request来找到一个合适的handler进行处理

getHandler会先调用抽象方法 getHandlerInternal 由子类实现查找逻辑,如果找不到,则查找默认配置的Hanlder,默认Hanlder找不到则返回null

SpringMVC默认使用了哪些HandlerMapping

如何查询Handler是在 DispacherServlet的getHandler方法操作的,它会在所有的handlerMappings(有序handlerMapping)中查找出一个可用的handler进行request的处理

如图可知会在7个handlerMapping中依次进行查询,这些handlerMapping是在 DispatcherServlet#initHandlerMappings() 设置的

定位到问题原因

跟踪请求代码发现对于不存在的url会在 SimpleUrlHanlderMapping中找到可用的handler,因为最终请求url匹配上了 /**,而 /** 模式对应的handler就是 ResourceHttpRequestHandler,此时getHandler的结果就是ResourceHttpRequestHandler 了,但是在上报时使用的是 HandlerMethod,执行到此时报了转换异常

WebMvcAutoConfiguration中的SourceHandler在哪注册的?

在启用MVC功能时,会增加注解 @EnableWebMvc,而对应的MVC配置类为 WebMvcConfigurationSupport,在这个类中进行SourceHandler的注册。跟踪代码发现两个ResourceHandler是在 WebMvcAutoConfigurationAdapter中添加的