feign 接口不能多重繼承問題 Only single-level inheritance supported

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 中將該邏輯改變就好

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章