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

    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")  同时使用在不同参数上。

 

posted @ 2019-08-08 13:11  星辰大海的征途  阅读(7594)  评论(1编辑  收藏  举报