Spring cloud Feign不支持对象传参解决办法[完美解决]

  • 2019 年 10 月 3 日
  • 笔记

    spring cloud 使用 Feign 进行服务调用时,不支持对象参数。

    通常解决方法是,要么把对象每一个参数平行展开,并使用 @RequestParam 标识出每一个参数,要么用 @RequestBody 将请求改为 body 传参,虽然这样解决了问题,但是这样限制了传参方式,并且使代码变得很繁重。

    以下为完美解决 Feign 对象传参问题的办法。

1. 引入如下依赖(可以在maven仓库中搜索 strongfeign)

1  2 <dependency>  3     <groupId>com.moonciki.strongfeign</groupId>  4     <artifactId>feign-httpclient</artifactId>  5     <version>10.2.3</version>  6 </dependency>

该源码修改自 https://github.com/OpenFeign/feign,提交过pr,但是项目原作者并没有采纳,pr地址如下:https://github.com/OpenFeign/feign/pull/949

之后为了同步到了maven 仓库,做了相应删减及pom的变更,具体改动可参考github。地址:https://github.com/cdmamata/strong-feign

注意:不要使用 10.3.x版本,该版本有问题。如果jar包无法下载请使用 maven 中央仓库。

2. 创建如下三个类

    开始时,打算把以下三个类加进仓库中,但由于如下三个类内容不多,并且有很多定制化的可能,因此单独实现。

    2.1 ParamModel.java

 1 package com.moonciki.strongfeign.model.annotation;   2   3 import java.lang.annotation.*;   4   5 @Target({ElementType.PARAMETER})   6 @Retention(RetentionPolicy.RUNTIME)   7 @Documented   8 public @interface ParamModel {   9     String value() default "";  10 }

    2.2 ModelExpander.java

 1 package com.moonciki.strongfeign.model.expander;   2   3 import com.alibaba.fastjson.JSON;   4 import feign.Param;   5 import lombok.extern.slf4j.Slf4j;   6   7 import java.util.Map;   8   9 @Slf4j  10 public class ModelExpander implements Param.Expander {  11  12     public String expand(Object value) {  13         String objectJson = JSON.toJSONString(value);  14         return objectJson;  15     }  16  17     @Override  18     public String expandWithName(Object value, String name) {  19         String valueExpand = null;  20  21         if(value != null){  22             if(name != null) {  23                 try {  24                     Map<String, Object> jsonMap = (Map<String, Object>)JSON.toJSON(value);  25  26                     Object getValue = jsonMap.get(name);  27                     if(getValue != null){  28                         valueExpand = getValue.toString();  29                     }  30                 } catch (Exception e) {  31                     log.error("GET VALUE ERROR:", e);  32                 }  33             }else {  34                 valueExpand = value.toString();  35             }  36         }  37  38         return valueExpand;  39     }  40 }

注:该类需依赖 fastjson,也可根据个人需要修改该方法。

    2.3 ParamModelParameterProcessor.java

 1 package com.moonciki.strongfeign.model.processor;   2   3 import com.moonciki.strongfeign.model.annotation.ParamModel;   4 import com.moonciki.strongfeign.model.expander.ModelExpander;   5 import feign.MethodMetadata;   6 import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;   7   8 import java.lang.annotation.Annotation;   9 import java.lang.reflect.Field;  10 import java.lang.reflect.Method;  11 import java.util.Collection;  12  13  14 public class ParamModelParameterProcessor implements AnnotatedParameterProcessor {  15  16     private static final Class<ParamModel> ANNOTATION = ParamModel.class;  17  18     public Class<? extends Annotation> getAnnotationType() {  19         return ANNOTATION;  20     }  21  22     @Override  23     public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {  24  25         int parameterIndex = context.getParameterIndex();  26         Class parameterType = method.getParameterTypes()[parameterIndex];  27         MethodMetadata data = context.getMethodMetadata();  28  29         Field[] fields = parameterType.getDeclaredFields();  30  31         for(Field field: fields) {  32             String name = field.getName();  33             context.setParameterName(name);  34  35             Collection query = context.setTemplateParameter(name, (Collection)data.template().queries().get(name));  36             data.template().query(name, query);  37         }  38         data.indexToExpander().put(context.getParameterIndex(), new ModelExpander());  39  40         return true;  41     }  42 }

 

3. 使用注解配置 feign Contract 对象

1     @Bean  2     public Contract feignContract(){  3         List<AnnotatedParameterProcessor> processors = new ArrayList<>();  4         processors.add(new ParamModelParameterProcessor());  5         processors.add(new PathVariableParameterProcessor());  6         processors.add(new RequestHeaderParameterProcessor());  7         processors.add(new RequestParamParameterProcessor());  8         return new SpringMvcContract(processors);  9     }

 

4. 使用方法示例

 1 @Primary   2 @FeignClient(value = "/user", fallback = UserClientFallback.class)   3 public interface UserClient {   4   5     /**   6      * demo post   7      * @return   8      */   9     @PostMapping("/demoPost")  10     Result demoPost(@ParamModel UserAccount userAccount);  11  12     /**  13      * demo get  14      * @return  15      */  16     @GetMapping("/demoGet")  17     Result demoPost(@ParamModel UserAccount userAccount);  18  19  20 }

使用时,只需要在对象前加 @ParamModel  注解即可

需要同时传递对象及基本类型参数时, @ParamModel 可以与 @RequestParam(“jobName”)  同时使用在不同参数上。