前言

最近在研究 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. JDK,JRE,JVM的区别与联系?

    概念区别 JDK:           Java Develpment Kit java 开发工具JRE:         Java Runtime Environment java运行时环境JVM: ...

  2. exists、in和join比较

    这个根据实际情况具体分析 遇到问题了再具体分析就行.

  3. Quartz任务调度的测试Demo1(含有配置文件的demo)

    Quartz是一个作业任务调度的框架,所在项目组中“消息推送模块”使用到此框架,于是写个demo熟悉下quart的用法: 使用Spring框架来集成Quartz的任务调度任务. 1.搭建Spring框 ...

  4. Linux常用命令基础

    linux 常用指令 基础命令 宿主目录 目录结构 文件管理 目录管理 用户管理 别名管理 压缩包管理 网络设置 shell技巧 帮助方法 /表示根目录 ~表示家目录 软件的安装(光盘中的软件呢): ...

  5. Linux的磁盘配额详解(Quota)

    1. 检查内核情况 检查当前内核是否支持quota,当前内核配置文件在/boot下 如果当前内核不支持quota,需要重新编译内核将quota support编译进核心: File systems   ...

  6. uboot环境变量

    一. uboot运行时环境变量分布 1.1. 环境变量有2份,一份在Flash中,另一份在DDR中.uboot开机时一次性从Flash中读取全部环境变量到DDR中作为环境变量的初始化值,然后使用过程中 ...

  7. [多校联考2019(Round 5 T1)] [ATCoder3912]Xor Tree(状压dp)

    [多校联考2019(Round 5)] [ATCoder3912]Xor Tree(状压dp) 题面 给出一棵n个点的树,每条边有边权v,每次操作选中两个点,将这两个点之间的路径上的边权全部异或某个值 ...

  8. 常用的 Python 标准库都有哪些?

    标准库:os 操作系统,time 时间,random 随机,pymysql 连接数据库,threading 线程,multiprocessing进程,queue 队列. 第三方库:django 和 f ...

  9. IBM公司的面试题,看看你能做出多少。

    进入IBM差不多是每一个IT人的梦想.IBM公司向来以高素质人才作为企业持续竞争力的保证,所以经常出一些千奇百怪的面试题,来考验一个人的综合能力,以下是5道IBM曾经出过的面试题,看看你能作出几道:  ...

  10. router4.0

    https://blog.csdn.net/sinat_17775997/article/details/69218382 React Router 4.0 实现路由守卫   https://www. ...