feign.Contract.BaseContract.parseAndValidatateMetadata
抛出的异常, 代码如下:
if (targetType.getInterfaces().length == 1) {
Util.checkState(targetType.getInterfaces()[0].getInterfaces().length == 0, "Only single-level inheritance supported: %s", new Object[]{targetType.getSimpleName()});
}
为何会做这个限制目前未知,感觉很奇怪,如果有人知道的话麻烦告诉一声。
解决办法
spring boot 默认会使用 SpringMvcContract 去实现
代码位置:org.springframework.cloud.openfeign.FeignClientsConfiguration
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
通过代码我们可以知道,如果我们自己提供一个实现,就可以替换掉
拷贝源码实现自己的Contract,然后把上面的验证注释掉;
编写一个 Configuration,注册自己实现的 Contract;
代码大概这样:
@Configuration
public class FeignConfig {
@Autowired(
required = false
)
private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList();
@Autowired(
required = false
)
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList();
@Bean
public Contract feignContract( ConversionService feignConversionService) {
return new MContract(this.parameterProcessors, feignConversionService);
}
@Bean
public FormattingConversionService feignConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
Iterator var2 = this.feignFormatterRegistrars.iterator();
while(var2.hasNext()) {
FeignFormatterRegistrar feignFormatterRegistrar = (FeignFormatterRegistrar)var2.next();
feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
}
解决方法-补充
按照上面的思路,只能保证你的代码能运行,但无法调用到具体的服务
因为会出现路径不正确的问题,本来应该是 /api/get 会变成 /get
源码位置,逻辑大概是:如果你有继承,使用继承类上的@RequestMapping
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
if (targetType.getInterfaces().length == 1) {
this.processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
this.processAnnotationOnClass(data, targetType);
而 processAnnotationOnClass 方法的实现,逻辑大概是 : 如果该类有继承就不处理。
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
if (clz.getInterfaces().length == 0) {
RequestMapping classAnnotation = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(clz, RequestMapping.class);
if (classAnnotation != null && classAnnotation.value().length > 0) {
String pathValue = Util.emptyToNull(classAnnotation.value()[0]);
pathValue = this.resolve(pathValue);
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().insert(0, pathValue);
}
}
}
与上面的逻辑进行对应,结果就是 如果接口有继承,只会使用父类的@RequestMapping
所以,最后只需要在自己实现的 MContract 中将该逻辑改变就好