场景复现
依赖:
Spring Boot 2.1.6.RELEASE
Eureka Client 2.1.0.RELEASE
OpenFeign 2.1.0.RELEASE
我们创建两个项目, ahao-server
服务提供方和ahao-client
服务调用方.
Eureka
可以使用我弄的一个开箱即用Eureka
在ahao-server
创建一个显示当前时间的controller
, 同时注册到eureka
上.
假设端口为http://localhost:8080
1 2 3 4 5 6 7 8
| @RestController @RequestMapping("/ahao") public class TimeController { @RequestMapping("/now") public String nowTime() { return "现在时间是:" + new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒").format(new Date()); } }
|
在ahao-client
创建一个controller
和一个feign
客户端, 同时注册到eureka
上.
假设端口为http://localhost:8081
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @RestController @RequestMapping("/ahao") public class TimeController { @Autowired private TimeApi timeApi; @RequestMapping("/now") public String now() { return timeApi.nowTime(); } }
@FeignClient(value = "AHAO-SERVER") @RequestMapping("/ahao") public interface TimeApi { @RequestMapping("/now") String nowTime(); }
|
运行ahao-client
报错java.lang.IllegalStateException: Ambiguous mapping.
我们改一下ahao-client
的controller
.
1 2 3 4 5 6 7 8 9 10
| @RestController @RequestMapping("/ahao") public class TimeController { @Autowired private TimeApi timeApi; @RequestMapping("/my-now") public String now() { return timeApi.nowTime(); } }
|
再运行ahao-client
就可以了.
我们访问http://localhost:8081/ahao/my-now
可以得到ahao-server
提供的时间服务, 但是访问http://localhost:8081/ahao/now
就是404
了.
那为什么会出现java.lang.IllegalStateException: Ambiguous mapping.
呢?
问题所在
我们看下RequestMappingHandlerMapping
映射注册器
1 2 3 4 5 6 7 8
| public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware { @Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } }
|
也就是, 只要Bean
类上有@Controller
注解或者@RequestMapping
注解, 那就会解析url
映射.
我们的TimeApi
上刚好有一个@RequestMapping
注解. 所以TimeApi
和TimeController
才会出现url
映射冲突.
我们改造成my-now
后, 就会有两个映射关系
/ahao/my-now
: 正常的访问ahao-server
服务
/ahao/now
: 访问失败404
看到这里肯定有疑问了, 为什么/ahao/now
有url
映射关系, 访问却404
?
我们再改造下TimeApi
. 加个@ResponseBody
注解, 看到这里应该就知道Spring MVC
做了多大的一件蠢事.
1 2 3 4 5 6 7
| @FeignClient(value = "AHAO-SERVER") @RequestMapping("/ahao") @ResponseBody public interface TimeApi { @RequestMapping("/now") String nowTime(); }
|
现在两个url
都可以正常访问了
/ahao/my-now
: 正常的访问ahao-server
服务
/ahao/now
: 正常的访问ahao-server
服务
解决方案
这是Spring MVC
的锅, Feign
是不可能改的了, 而且Spring MVC
也不可能改, 因为要兼容以前版本的使用者.
最简单的方法
不要把@RequestMapping
和@FeignClient
一起用, 直接把链接拼接到方法级的@RequestMapping
上
1 2 3 4 5
| @FeignClient(value = "AHAO-SERVER") public interface TimeApi { @RequestMapping("/ahao/now") String nowTime(); }
|
或者用@FeignClient
的path
属性
1 2 3 4 5
| @FeignClient(value = "AHAO-SERVER", path = "/ahao") public interface TimeApi { @RequestMapping("/now") String nowTime(); }
|
装逼用方法
来源: https://github.com/spring-cloud/spring-cloud-netflix/issues/466#issuecomment-257043631
但是失去了自动装配的一些特性, 不推荐使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration @ConditionalOnClass({Feign.class}) public class FeignMappingDefaultConfiguration { @Bean public WebMvcRegistrations feignWebRegistrations() { return new WebMvcRegistrationsAdapter() { @Override public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { return new FeignFilterRequestMappingHandlerMapping(); } }; }
private static class FeignFilterRequestMappingHandlerMapping extends RequestMappingHandlerMapping { @Override protected boolean isHandler(Class<?> beanType) { return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, FeignClient.class) == null); } } }
|
参考资料