1. 程式人生 > >Spring cloud Feign不支援物件傳參解決辦法[完美解決]

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")  同時使用在不同引數上。

&n