前言

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

环境

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

解决方案

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

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

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

  1. import feign.MethodMetadata;
  2. import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
  3. import org.springframework.stereotype.Component;
  4.  
  5. import java.lang.annotation.Annotation;
  6. import java.lang.reflect.Field;
  7. import java.lang.reflect.Method;
  8. import java.util.Collection;
  9.  
  10. /**
  11. * Created by qingyun.yu on 2018/9/4.
  12. */
  13. @Component
  14. public class GetParamParameterProcessor implements AnnotatedParameterProcessor {
  15. private static final Class<GetParam> ANNOTATION = GetParam.class;
  16. @Override
  17. public Class<? extends Annotation> getAnnotationType() {
  18. return ANNOTATION;
  19. }
  20.  
  21. @Override
  22. public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
  23. int parameterIndex = context.getParameterIndex();
  24. Class parameterType = method.getParameterTypes()[parameterIndex];
  25. MethodMetadata data = context.getMethodMetadata();
  26. Field[] fields = parameterType.getDeclaredFields();
  27. for(Field field: fields) {
  28. String name = field.getName();
  29. context.setParameterName(name);
  30. Collection query = context.setTemplateParameter(name, (Collection)data.template().queries().get(name));
  31. data.template().query(name, query);
  32. }
  33. return true;
  34. }
  35. }

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

  1. import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
  2. import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
  3. import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6.  
  7. /**
  8. * Created by qingyun.yu on 2018/9/4.
  9. */
  10. @Configuration
  11. public class FeignConfig {
  12. @Bean
  13. public PathVariableParameterProcessor getPathVariableParameterProcessor() {
  14. return new PathVariableParameterProcessor();
  15. }
  16.  
  17. @Bean
  18. public RequestParamParameterProcessor getRequestParamParameterProcessor() {
  19. return new RequestParamParameterProcessor();
  20. }
  21.  
  22. @Bean
  23. public RequestHeaderParameterProcessor getRequestHeaderParameterProcessor() {
  24. return new RequestHeaderParameterProcessor();
  25. }
  26. }

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

  1. private boolean isGetUrlParam(Object value, RequestTemplate mutable) {
  2. if(mutable.method() != "GET") {
  3. return false;
  4. }
  5. switch (value.getClass().getSimpleName()) {
  6. case "Integer":
  7. return false;
  8. case "String":
  9. return false;
  10. case "Boolean":
  11. return false;
  12. case "Float":
  13. return false;
  14. case "Long":
  15. return false;
  16. case "Character":
  17. return false;
  18. case "Double":
  19. return false;
  20. case "Byte":
  21. return false;
  22. case "Short":
  23. return false;
  24. case "Date":
  25. return false;
  26. case "BigDecimal":
  27. return false;
  28. default:
  29. System.out.println("value is object param");;
  30. }
  31. return true;
  32. }
  33.  
  34. private Map<String, Object> getObjectParam(Object obj) {
  35. Field[] fields = obj.getClass().getDeclaredFields();
  36. Map<String, Object> urlParams = new HashMap<>();
  37. for (Field field : fields) {
  38. field.setAccessible(true);
  39. try {
  40. Object value = field.get(obj);
  41. if (value == null) {
  42. urlParams.put(field.getName(), "");
  43. } else {
  44. urlParams.put(field.getName(), value);
  45. }
  46. } catch (Exception e) {
  47. throw new RuntimeException(e);
  48. }
  49. }
  50. return urlParams;
  51. }

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

  1. @Override
  2. public RequestTemplate create(Object[] argv) {
  3. RequestTemplate mutable = new RequestTemplate(metadata.template());
  4. if (metadata.urlIndex() != null) {
  5. int urlIndex = metadata.urlIndex();
  6. checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
  7. mutable.insert(0, String.valueOf(argv[urlIndex]));
  8. }
  9. Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
  10. for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
  11. int i = entry.getKey();
  12. Object value = argv[entry.getKey()];
  13.  
  14. Map<String, Object> urlParams = null;
  15. if (isGetUrlParam(value, mutable)) {
  16. urlParams = getObjectParam(value);
  17. }
  18.  
  19. if (value != null) { // Null values are skipped.
  20. if (indexToExpander.containsKey(i)) {
  21. value = expandElements(indexToExpander.get(i), value);
  22. }
  23. for (String name : entry.getValue()) {
  24. if (isGetUrlParam(value, mutable)) {
  25. varBuilder.put(name, urlParams.get(name));
  26. } else {
  27. varBuilder.put(name, value);
  28. }
  29. }
  30. }
  31. }
  32.  
  33. RequestTemplate template = resolve(argv, mutable, varBuilder);
  34. if (metadata.queryMapIndex() != null) {
  35. // add query map parameters after initial resolve so that they take
  36. // precedence over any predefined values
  37. Object value = argv[metadata.queryMapIndex()];
  38. Map<String, Object> queryMap = toQueryMap(value);
  39. template = addQueryMapQueryParameters(queryMap, template);
  40. }
  41.  
  42. if (metadata.headerMapIndex() != null) {
  43. template =
  44. addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
  45. }
  46.  
  47. return template;
  48. }

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

  1. <dependency>
  2. <groupId>com.yun.demo</groupId>
  3. <artifactId>feign-core</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. </dependency>

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

  1. <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请求的对象上就可以了。

  1. import com.yun.demo.annotation.GetParam;
  2. import com.yun.demo.entity.User;
  3. import com.yun.demo.fallback.UserClientFallback;
  4. import org.springframework.cloud.openfeign.FeignClient;
  5. import org.springframework.web.bind.annotation.*;
  6.  
  7. /**
  8. * Created by qingyun.yu on 2018/9/2.
  9. */
  10. @FeignClient(name = "feign-service", fallback = UserClientFallback.class)
  11. public interface UserClient {
  12.  
  13. @RequestMapping(value = "user", method = RequestMethod.GET)
  14. User getUser(@GetParam User user);
  15.  
  16. }

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

[spring cloud feign] [bug] 使用对象传输get请求参数的更多相关文章

  1. Spring Cloud Feign 如何使用对象参数

    概述 Spring Cloud Feign 用于微服务的封装,通过接口代理的实现方式让微服务调用变得简单,让微服务的使用上如同本地服务.但是它在传参方面不是很完美.在使用 Feign 代理 GET 请 ...

  2. Bug集锦-Spring Cloud Feign调用其它接口报错

    问题描述 Spring Cloud Feign调用其它服务报错,错误提示如下:Failed to instantiate [java.util.List]: Specified class is an ...

  3. Spring cloud Feign 深度学习与应用

    简介 Spring Cloud Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单.Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解 ...

  4. 笔记:Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  5. 笔记:Spring Cloud Feign 其他配置

    请求压缩 Spring Cloud Feign 支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,我们只需要通过下面二个参数设置,就能开启请求与响应的压缩功能,yml配置格式如下: fei ...

  6. 笔记:Spring Cloud Feign 声明式服务调用

    在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以我们通常会针对各个微服务自行封装一些客户端类来包装这些依赖服务的调用,Spring Cloud Feign 在此基础上做了进 ...

  7. 第六章:声明式服务调用:Spring Cloud Feign

    Spring Cloud Feign 是基于 Netflix Feign 实现的,整合了 Spring Cloud Ribbon 和 Spring Cloud Hystrix,除了提供这两者的强大功能 ...

  8. Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  9. RestTemplate OR Spring Cloud Feign 上传文件

    SpringBoot,通过RestTemplate 或者 Spring Cloud Feign,上传文件(支持多文件上传),服务端接口是MultipartFile接收. 将文件的字节流,放入ByteA ...

随机推荐

  1. [转帖]NetSuite 进入中国市场满一年,甲骨文公布首份成绩单

    NetSuite 进入中国市场满一年,甲骨文公布首份成绩单 https://baijiahao.baidu.com/s?id=1617073148682281883&wfr=spider&am ...

  2. swtich和case语句中,定义变量要加花括号

    转自: http://blog.chinaunix.net/uid-27103408-id-3340702.html http://www.xuebuyuan.com/2070170.html swi ...

  3. CodeForces - 714E + POJ - 3666 (dp严格单调递增与非严格单调递增)

    左偏树 炒鸡棒的论文<左偏树的特点及其应用> 虽然题目要求比论文多了一个条件,但是……只需要求非递减就可以AC……数据好弱…… 虽然还没想明白为什么,但是应该觉得应该是这样——求非递减用大 ...

  4. wordpress开发的一些积累

    wordpress 攒知识点 记录开发 wordpress 的一些技能点,以备不时之需 短代码 Shortcode 虽然很多插件都是提供,直接在代码中插入类似[Shortcode] 便可以生效,但是很 ...

  5. C#选择文件返回缩略图

    传入文件路径,返回临时文件中缩略图的路径,jpg,pdf,office,rar都行 string path = ThumbnailHelper.GetInstance().GetJPGThumbnai ...

  6. Label显示时间

    package 第十一章; import java.awt.Button; import java.awt.Color; import java.awt.Font; import java.awt.F ...

  7. Android数据库使用指南(下)

    前言 上面已经说了,对表进行修改,其实就是对数据库进行升级,删除表也算升级啊,反正就是发生变化,数据库就需要升级. 所以老实说其实有个地方决定了数据库的版本 public class DBHelper ...

  8. setState总结

    react中的setState特点: 是异步操作函数: 组件在还没有渲染之前, this.setState 还没有被调用: 批量执行 State 转变时让 DOM 渲染更快(相对比一个一个的setSt ...

  9. AtCoder Beginner Contest 089 D - Practical Skill Test

    Problem Statement We have a grid with H rows and W columns. The square at the i-th row and the j-th ...

  10. 1126. Eulerian Path (25)

    In graph theory, an Eulerian path is a path in a graph which visits every edge exactly once. Similar ...