前言

最近在研究 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&总结5

    老师:lijin2019,助教:晨晨果 提交作业 实验三 String类的应用 实验目的 掌握类String类的使用: 学会使用JDK帮助文档: 实验内容 1.已知字符串:"this is ...

  2. Mybatis-学习笔记(N)mybatis-generator 生成DAO、Mapper、entity

    1.mybatis-generator 生成DAO.Mapper.entity 所需环境:jdk 所需jar包:mybatis-generator-core-1.3.5.jar.MySQL-conne ...

  3. P3958奶酪

    这个题是2017noip提高组的真题,是一个深度搜索题,得分轨迹:10-80-100pts. 在三维空间里,存在可能连通的洞(半径r),问是否可以通过这些洞从底部到达顶部.一开始我联想到了01迷宫,以 ...

  4. while循环和字符串格式化

    小知识点 \n#换行 \t #制表 \r #回车 print(a,b,c,d,sep="\n")换行 sep默认空格 1.while--关键字(死循环) while 空格 条件: ...

  5. 对于出现拒绝访问root用户的解决方案

    提示:ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'mysql'   由于使用mysql -u root ...

  6. 去掉img与img之间,video与video之间默认的间距(3种方式)

    img,video{ /*第1种方式*/ border:; vertical-align: bottom; /*第2种方式*/ outline-width:0px; vertical-align:to ...

  7. linux目录结构详细补充

    Linux各目录及每个目录的详细介绍 [常见目录说明] Linux目录和Windows目录有着很大的不同,Linux目录类似一个树,最顶层是其根目录,如下图: /bin 二进制可执行命令 /dev 设 ...

  8. 锋利的jQuery ——jQuery中的DOM操作(三)

    一.DOM的操作分类 1>DOM Core   2>HTML-DOM   3>CSS-DOM 二.jQuery中的DOM操作 DOM树 ①查找节点 1)查找元素节点 利用jQuery ...

  9. JAVA中关于日期的最常见的操作

    //获取当前系统的时间戳 long times = System.currentTimeMillis(); //创建一个当前日期对象 Date now = new Date(); //基于指定的时间戳 ...

  10. man - 格式化并显示在线帮助手册页

    总览 man [-acdfFhkKtwW] [-m 系统名] [-p <前处理程序>] [-C <配置文件>] [-M <路径>] [-P <浏览方式> ...