前言

最近在研究 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. JS基础篇--JS获取元素的宽高以及offsetTop,offsetLeft等的属性值

    $(obj).width()与$(obj).height() $(obj).width()与$(obj).height() :jquery方式获取元素的宽高,不包括滚动条与工具条 $(obj).wid ...

  2. P1056排坐椅

    这是2008普及组真题,是一个提高—的模拟. 仔细读完题便有了思路:累放在i行能隔开wi个,比较排序wi,输出即可.所以在这里遇到了结构体排序的问题与手写cmp的问题.对于两个语法知识掌握得都不好,所 ...

  3. echarts markLine 辅助线非直线设置

    效果图: 用例option: option = { title: { text: '未来一周气温变化', subtext: '纯属虚构' }, tooltip: { trigger: 'axis' } ...

  4. P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)

    P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...

  5. css实现缩放自适应网页--手机web

    一. 允许网页宽度自动调整: "自适应网页设计"到底是怎么做到的? 其实并不难. 首先,在网页代码的头部,加入一行viewport元标签. <meta name=" ...

  6. git设置Eclipse中忽略的文件

    GitHub 官网样例文件https://github.com/github/gitignorehttps://github.com/github/gitignore/blob/master/Java ...

  7. 命令行执行php脚本中的$argv和$argc配置方法

    在实际工作中有可能会碰到需要在nginx命令行执行php脚本的时候,当然你可以去配置一个conf用外网访问. 在nginx命令行中 使用 php index.php 就可以执行这个index.php脚 ...

  8. grep 查找文件

    --递归查找目录下含有该字符串的所有文件 grep -rn "data_chushou_pay_info" /home/hadoop/nisj/automationDemand/ ...

  9. 使用Medusa美杜莎暴力破解SSH密码

    使用Medusa美杜莎暴力破解SSH密码 1.Medusa简介 Medusa(美杜莎)是一个速度快,支持大规模并行,模块化的爆力破解工具.可以同时对多个主机,用户或密码执行强力测试.Medusa和hy ...

  10. rsync服务实践

    RSYNC数据备份 RSYNC=Remote Sync 远程同步   高效,一定要结合shell 官方网站:https://rsync.samba.org/ Author:     Andrew Tr ...