zoukankan      html  css  js  c++  java
  • [spring cloud feign] [bug] 使用对象传输get请求参数

    前言

    最近在研究 srping cloud feign ,遇到了一个问题,就是当 get 请求 的参数使用对象接收时,就会进入熔断返回。经过百度,发现网上大部分的解决方案都是将请求参数封装到RequestBody里面进行传输。但感觉这种方式并不怎么优雅。所以自己就研究了研究,以下是我给出的方案。有什么不对的地方还希望大家指正。

    环境

    • java版本:8
    • spring cloud:Finchley.RELEASE

    解决方案

    1. 首先我们创建一个注解 GetParam ,用于将参数相关的信息封装到 RequestTemplate 。

    import java.lang.annotation.*;
    
    /**
     * Created by qingyun.yu on 2018/9/4.
     */
    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface GetParam {
        Class value() default Object.class;
    }

    2. 创建注解处理器 GetParamParameterProcessor ,并且注册到spring容器中,主要逻辑是将参数相关信息封装到 Template 。

    import feign.MethodMetadata;
    import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
    import org.springframework.stereotype.Component;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.Collection;
    
    /**
     * Created by qingyun.yu on 2018/9/4.
     */
    @Component
    public class GetParamParameterProcessor implements AnnotatedParameterProcessor {
        private static final Class<GetParam> ANNOTATION = GetParam.class;
        @Override
        public Class<? extends Annotation> getAnnotationType() {
            return ANNOTATION;
        }
    
        @Override
        public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
            int parameterIndex = context.getParameterIndex();
            Class parameterType = method.getParameterTypes()[parameterIndex];
            MethodMetadata data = context.getMethodMetadata();
            Field[] fields = parameterType.getDeclaredFields();
            for(Field field: fields) {
                String name = field.getName();
                context.setParameterName(name);
                Collection query = context.setTemplateParameter(name, (Collection)data.template().queries().get(name));
                data.template().query(name, query);
            }
            return true;
        }
    }

    3.  创建 FeignConfig ,用于将Spring的参数注解处理器注册到Spring中。

    import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
    import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
    import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * Created by qingyun.yu on 2018/9/4.
     */
    @Configuration
    public class FeignConfig {
        @Bean
        public PathVariableParameterProcessor getPathVariableParameterProcessor() {
            return new PathVariableParameterProcessor();
        }
    
        @Bean
        public RequestParamParameterProcessor getRequestParamParameterProcessor() {
            return new RequestParamParameterProcessor();
        }
    
        @Bean
        public RequestHeaderParameterProcessor getRequestHeaderParameterProcessor() {
            return new RequestHeaderParameterProcessor();
        }
    }

    4.修改 io.github.openfeign:feign-core 的源码,在 ReflectiveFeign 类中增加如下代码,源码地址点这里,也可以直接用我的demo里面的代码。

    private boolean isGetUrlParam(Object value, RequestTemplate mutable) {
          if(mutable.method() != "GET") {
            return false;
          }
          switch (value.getClass().getSimpleName()) {
            case "Integer":
              return false;
            case "String":
              return false;
            case "Boolean":
              return false;
            case "Float":
              return false;
            case "Long":
              return false;
            case "Character":
              return false;
            case "Double":
              return false;
            case "Byte":
              return false;
            case "Short":
              return false;
            case "Date":
              return false;
            case "BigDecimal":
              return false;
            default:
              System.out.println("value is object param");;
          }
          return true;
        }
    
        private Map<String, Object> getObjectParam(Object obj) {
          Field[] fields = obj.getClass().getDeclaredFields();
          Map<String, Object> urlParams = new HashMap<>();
          for (Field field : fields) {
            field.setAccessible(true);
            try {
              Object value = field.get(obj);
              if (value == null) {
                urlParams.put(field.getName(), "");
              } else {
                urlParams.put(field.getName(), value);
              }
            } catch (Exception e) {
              throw new RuntimeException(e);
            }
          }
          return urlParams;
        }

    并且将 RequestTemplate create(Object[] argv) 代码替换成如下代码。

    @Override
        public RequestTemplate create(Object[] argv) {
          RequestTemplate mutable = new RequestTemplate(metadata.template());
          if (metadata.urlIndex() != null) {
            int urlIndex = metadata.urlIndex();
            checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
            mutable.insert(0, String.valueOf(argv[urlIndex]));
          }
          Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
          for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
            int i = entry.getKey();
            Object value = argv[entry.getKey()];
    
            Map<String, Object> urlParams = null;
            if (isGetUrlParam(value, mutable)) {
              urlParams = getObjectParam(value);
            }
    
            if (value != null) { // Null values are skipped.
              if (indexToExpander.containsKey(i)) {
                value = expandElements(indexToExpander.get(i), value);
              }
              for (String name : entry.getValue()) {
                if (isGetUrlParam(value, mutable)) {
                  varBuilder.put(name, urlParams.get(name));
                } else {
                  varBuilder.put(name, value);
                }
              }
            }
          }
    
          RequestTemplate template = resolve(argv, mutable, varBuilder);
          if (metadata.queryMapIndex() != null) {
            // add query map parameters after initial resolve so that they take
            // precedence over any predefined values
            Object value = argv[metadata.queryMapIndex()];
            Map<String, Object> queryMap = toQueryMap(value);
            template = addQueryMapQueryParameters(queryMap, template);
          }
    
          if (metadata.headerMapIndex() != null) {
            template =
                addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
          }
    
          return template;
        }

    5. 编译打包 feign-core ,并且将其引入工程里面。

    <dependency>
        <groupId>com.yun.demo</groupId>
        <artifactId>feign-core</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    注意要将spring cloud的原本引入的 feign-core 去除掉。

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <exclusions>
    <exclusion>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-core</artifactId>
    </exclusion>
    </exclusions>
    </dependency>

    6. 使用时将注解注入到get请求的对象上就可以了。

    import com.yun.demo.annotation.GetParam;
    import com.yun.demo.entity.User;
    import com.yun.demo.fallback.UserClientFallback;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * Created by qingyun.yu on 2018/9/2.
     */
    @FeignClient(name = "feign-service", fallback = UserClientFallback.class)
    public interface UserClient {
    
        @RequestMapping(value = "user", method = RequestMethod.GET)
        User getUser(@GetParam User user);
    
    }

    下面附上我的git地址,里面是我写的demo,有什么不妥的地方还希望各位大虾指正。

  • 相关阅读:
    使用字体图标完整步骤
    用position:absolute定位小窗口位于版面正中心
    MySql 技术内幕 (第7章 游标)
    MySql 技术内幕 (第5章 联接与集合操作)
    赋值语句作为判断的条件
    发布订阅模式和观察者模式
    关系代数
    数据库关系代数表达式学习
    软考通过分数
    哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度
  • 原文地址:https://www.cnblogs.com/cafebabe-yun/p/9588323.html
Copyright © 2011-2022 走看看