问题关键词: Ambiguous mapping. Cannot map OpenFeign @RequestMapping ==@FeignClient==
我们在开发微服时候,api层作为OpenFeign的二方接口SDK,服务的实现 OpenFeign定义的接口实现在Controller上。这种模式开始没毛病,实则问题挺棘手的,因为@RequestMapping注解会继承到Controller,OpenFeign接口上的@RequestMapping注解也会被接口地址注册器扫描到,从而导致URL地址冲突。
FeignClient类,用于提供给司内二方调用的内部接口
@FeignClient(name = "backstage-server",contextId = "sysUserApi")
@RequestMapping("/inner/sysUser-api")
public interface SysUserClient {
   @GetMapping("/sysUser/check")
&nbsp; &nbsp;Resp<boolean>checkUser(@RequestParam(&quot;staffId&quot;) Long staffId);
}
SysUserController 实现SysUserClient的API的控制器
@RestController
public class SysUserController implements SysUserClient {
&nbsp; &nbsp;
&nbsp; &nbsp;Resp<boolean>checkUser(@RequestParam(&quot;staffId&quot;) Long staffId){
&nbsp; &nbsp; &nbsp; &nbsp;return xxxx;
&nbsp; &nbsp;}
}
运行后出现异常
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'com.xxxx.tech.backstage.controller.SysUserController' method
com.xxxx.tech.backstage.feign.BackstageSysUserApi#getSysUserList(List)
to {POST /inner/sysUser-api/sysUser/get/list}: There is already 'com.xxxx.platform.user.goods.server.client.SysUserClient' bean method
com.xxxx.platform.user.goods.server.client.SysUserClient#getSysUserList(List) mapped.
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.validateMethodMapping(AbstractHandlerMethodMapping.java:636)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:603)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:318)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.registerHandlerMethod(RequestMappingHandlerMapping.java:378)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.registerHandlerMethod(RequestMappingHandlerMapping.java:75)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lambda$detectHandlerMethods$1(AbstractHandlerMethodMapping.java:288)
at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:286)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.processCandidateBean(AbstractHandlerMethodMapping.java:258)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:217)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:205)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:189)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1858)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1795)
... 51 common frames omitted
问题原因前面已经分析了,那么现在要解决该问题只需要在注册URL过程将重复扫描的URL排除在外即可。
这里会使用到WebMvcRegistrations接口扩展注册WebURL能力。
需要排除的URL注册的主要特征为: 1、是java定义的interface ; 2、带有@FeignClient注解并加了@RequestMapping注解;
新建类 WebMvcFeignClientExcludeRegistrations 排除在接口上带有FeignClient注解的URL注册
package com.xxx.tech.common.feign.config;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
* 排除不需要的mapping,例如feignclient注解的mapping
*<p>* 自动配置@FeignClient 注解的 接口地址扫描。
* 解决问题:@FeignClient的接口,在Controller实现api接口 @RequestMapping会继承到controller类上,造成路径冲突。
*
* @author marker
*/
public class WebMvcFeignClientExcludeRegistrations implements WebMvcRegistrations {
&nbsp; &nbsp;@Override
&nbsp; &nbsp;public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
&nbsp; &nbsp; &nbsp; &nbsp;return new FeignRequestMappingHandlerMapping();
&nbsp; &nbsp;}
&nbsp; &nbsp;private static class FeignRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
&nbsp; &nbsp; &nbsp; &nbsp;@Override
&nbsp; &nbsp; &nbsp; &nbsp;protected boolean isHandler(Class<?> beanType) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 如果是接口且有openfeign注解的视为无效扫描
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (beanType.isInterface() &amp;&amp; AnnotatedElementUtils.hasAnnotation(beanType, FeignClient.class)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return false;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return super.isHandler(beanType);
&nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp;}
}
加入到装配bean中
&nbsp; &nbsp;/**
&nbsp; &nbsp; * 自动配置@FeignClient 注解的 接口地址扫描。
&nbsp; &nbsp; * 解决问题:@FeignClient的接口,在Controller实现api接口 @RequestMapping会继承到controller类上,造成路径冲突。
&nbsp; &nbsp; * @return
&nbsp; &nbsp; */
&nbsp; &nbsp;@Bean
&nbsp; &nbsp;@ConditionalOnMissingBean
&nbsp; &nbsp;public WebMvcFeignClientExcludeRegistrations webMvcFeignClientExcludeRegistrations(){
&nbsp; &nbsp; &nbsp; &nbsp;return new WebMvcFeignClientExcludeRegistrations();
&nbsp; &nbsp;}
调试以上代码,所有的URL接口地址扫描都会经过FeignRequestMappingHandlerMapping 判断接口注解是否符合标准
</?>