思君令人老,努力加餐饭
1 前言
今天在项目开发中遇到了一个 Ambiguous mapping
问题,错误信息展示为 Ambiguous mapping. Cannot map 'xxxController' method
。起因是项目(项目是微服务架构,需要引入其他项目的 api)开发中使用了其它项目的 api jar 包,在控制器层声明的 RequestMapping
和 jar 包中的 RequestMapping
一致造成的,项目启动时,会扫描所有的包并创建 RequestMappingHandler
,在 doCreateBean
的 initializeBean
阶段发生异常。
2 问题分析
遇到的报错信息如下图所示,当看到这样的信息时,真是一头雾水。原因就是在 MemberOrderController
中的方法和引入 jar 包中的 MemberOrderApi
中的方法使用的路径是一样的。那么在两者路径都不变动的情况下,怎么去修改这个问题呢?
这里需要介绍一下 RequestMappingInfo
, 可以从下图看到在控制器层写的每一个 Mapping
都可以在 RequestMappingInfo
中找到对应的属性,其包含了所有的映射信息。
异常出现在 Spring 的 refresh 阶段,RequestMappingHandlerMapping
实现了 Bean
初始化的接口 InitializingBean
,在 bean
加载完成后会自动调用 afterPropertiesSet
方法进行后置处理,在此方法中调用了 initHandlerMethods
,异常的触发是在以下链路中。
RequestMappingHandlerMapping.registerHandlerMethod
AbstractHandlerMethodMapping.registerHandlerMethod
AbstractHandlerMethodMapping.validateMethodMapping
在调用 validateMethodMapping
是传进来的 mapping
的类型即 RequestMappingInfo
, 会从 mappingLookup
中查找对应的 HandlerMethod
来判断是否存在。
通过上面的分析,我们知道要想解决这样的报错,在请求路径不变的情况下,能修改的只有 RequestMappingInfo
中的属性,包括请求头,参数,名称、consumes
和 produces
等。任意修改一项即可实现项目中存在同样的请求路径,不过在客户端发起请求时,需要严格按照 mapping
的参数要求来请求,才能执行对应的方法。这里最常用的就是同一个请求路径,采用不同的方法(post
和 get
)也可以实现。
3 RequestMappingHandlerMapping
在上述问题分析中,我们知道了 RequestMappingHandlerMapping
在处理 Controller
中的重要性,在本节中将分享如何利用其在项目启动时打印应用的所有接口。在项目中创建配置文件 AppHandlerMapping
实现集成,重写 registerHandlerMethod
方法即可获取到对应的 requestmapping
信息。通过以上的操作,既可以打印项目中所有的接口信息。
除了打印项目接口列表外,还可以通过自定义 RestMapping
来处理请求,主要是为了了解 RequestMappingHandlerMapping
的作用,方便深入理解其原理和逻辑思想,可以更好的解决实际开发过程中遇到的问题,在遇到棘手的问题时,可以利用其特点找到巧妙的解决方案。
如下图所示,通过重写 getMappingForMethod
方法,可以实现自定义注解的解析,通过获取其特定的属性,重新构建 RequestMappingInfo
对象,即可实现特定接口的注册和功能实现。这在实际的开发中使用比较少,但是可以作为一项技术储备来学习。在实际开发过程中,可以根据需要对 SpringMvc 的功能点进行拓展,实现特定的业务需求。这里需要注意的是 AnnotatedElementUtils.findMergedAnnotation
,这是一个很好用的工具类,可以通过它找到方法上的注解。
4 总结
在本文主要分享了 SpringMVC-Ambiguous mapping
的问题解决方法,以及出现该问题的原因。由此引出了 RequestMappingHandlerMapping
,通过其简单实现了项目接口的打印和自定义注解解析。本文中所涉及的代码已经上传至 github
, 欢迎交流学习。项目地址 springboot-auth。