前言

最近在研究 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,有什么不妥的地方还希望各位大虾指正。

[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. (3.4)常用知识-char与varchar的选择

    1.char与varchar的比较 (1)数据存储开销 [1]varchar列需要2个额外的字节来记录存储数据的长度 [2]每个可为null的char列,需要一些字节(空位图)来反应数据的为空性 [3 ...

  2. CentOS7中MySQL跨机器数据迁移

    1.概况 在CentOS7环境下,使用命令方式将MySQL数据从源端主机迁移到目标端主机上. 2.迁移全部数据库 1)源端备份: [root@hadoop102 /]# mysqldump -u ro ...

  3. qt undefined reference to `vtable for subClass'

    1. 建立一个console工程 QT -= gui CONFIG += c++ console CONFIG -= app_bundle # The following define makes y ...

  4. django项目学习之QQ登录

    最近在用django框架写一个商城项目(前后端分离),里面用到的一些技术其他项目也可以借鉴,于是就想写一些博客记录,以防自己忘记,今天先写一个关于登录接口中引入QQ登录接口的流程. 关于QQ登录接口的 ...

  5. 如何将数据库导入到本地MySQL

    有两个方法:(1)在MySQL的客户端进行导入,比如: http://jingyan.baidu.com/article/6dad507517c11aa123e36ea0.html (2)方法:常用s ...

  6. go & cron

    https://github.com/robfig/cron - 源码 cron - GoDoc 参考 go---定时任务 cron,gin 静态文件 go语言里比较好用的计划任务调度模块

  7. 【C#】获取"我的电脑"的名字,如This PC、这台计算机

    原文:[C#]获取"我的电脑"的名字,如This PC.这台计算机 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接: ...

  8. C语言双向链表讲解

    一.双向链表的概念 双向链表基于单链表.单链表是单向的,有一个头结点,一个尾结点,要访问任何结点,都必须知道头结点,不能逆着进行.而双链表添加了一个指针域,通过两个指针域,分别指向结点的前结点和后结点 ...

  9. Linux硬盘分区和软硬链接

    磁盘分区:先在虚拟机设置里添加一个新硬盘 查看现在电脑上有几个硬盘:ls /dev/硬盘名 分区命令:fdisk  /dev/硬盘名 进入之后,可以输入 m 查看命令的具体是使用 新建分区:n→p(选 ...

  10. 001-supervisor

    supervisor 使用教程(转) 原文地址:https://word.gw1770df.cc/2016-08-04/linux/supervisor-%E4%BD%BF%E7%94%A8%E6%9 ...