首页 > 其他分享 >feign接口中使用泛型接收返回值

feign接口中使用泛型接收返回值

时间:2024-06-17 16:28:06浏览次数:28  
标签:feign return returnObject public import 泛型 返回值 type response

问题

一般在对接客户接口时,都会使用多个策略类处理,并且不同的客户返回值不同,因此比较好的方案是使用泛型来处理。

然而现实是,feign或openfeign会擦除泛型,将返回的对象转为LinkedHashMap,导致转json反序列化成对象时异常。

思路

debug可以发现,在decode时,response返回的是所需要数据,问题在于反序列化,所以要在decode时,针对反序列化做一些处理。

在参考一些资料和文章后,最终最适合我的方案是在请求头加泛型类的全限定名,decode时使用json转换成所需类。

流程如下:

  1. 发送请求在header加入泛型类名
  2. 自定义decode取出header,反序列化为所需类
  3. 返回结果

准备

服务端需要统一返回值,比如我自定义的统一返回值类型为R

主要代码

Decoder

import com.fasterxml.jackson.databind.JsonNode;
import feign.FeignException;
import feign.Response;
import feign.codec.Decoder;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


public class FeignGenericDecoder implements Decoder {

    public static final String GENERICS_HEADER = "generics-header";
    private final Decoder decoder;

    public FeignGenericDecoder(@Lazy Decoder decoder) {
        this.decoder = decoder;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    @SneakyThrows
    @Override
    public Object decode(Response response, Type type) throws FeignException {
//        Annotation[] annotations = response.request().requestTemplate().methodMetadata().method().getAnnotations();
        Object returnObject;
        if (isParameterizeHttpEntity(type)) {
            type = ((ParameterizedType) type).getActualTypeArguments()[0];
            Object decodedObject = decoder.decode(response, type);

            returnObject = createResponse(decodedObject, response);
        } else if (isHttpEntity(type)) {
            returnObject = createResponse(null, response);
        } else {
            returnObject = decoder.decode(response, type);
        }

        // 以上是原默认实现,复制过来,为了拿到returnObject
        if (returnObject instanceof R) {
            Map<String, Collection<String>> map = response.request().headers();
            Collection<String> generics = map.get(GENERICS_HEADER);
            if (CollectionUtils.isEmpty(generics)) {
                return returnObject;
            }

            // 返回值转json
            String body = JacksonUtils.toJson(returnObject);
            JsonNode jsonNode = JacksonUtils.getMapper().readTree(body);
            if (jsonNode.get("code").intValue() != 200) {
                return returnObject;
            }

            Class<?> clazz = Class.forName(generics.iterator().next());
            // 拿出result,实例化对象(genericsHeader 指定的类型),然后重新set
            JsonNode data = jsonNode.get("data");
            if (data.isArray()) {
                List<?> list = JacksonUtils.toList(data.toString(), clazz);
                ((R) returnObject).setData(list);
            } else if (data.isObject()) {
                Object obj = JacksonUtils.toObj(data.toString(), clazz);
                ((R) returnObject).setData(obj);
            }
        }
        return returnObject;
    }

    private boolean isParameterizeHttpEntity(Type type) {
        if (type instanceof ParameterizedType) {
            return isHttpEntity(((ParameterizedType) type).getRawType());
        }
        return false;
    }

    private boolean isHttpEntity(Type type) {
        if (type instanceof Class) {
            Class<?> c = (Class<?>) type;
            return HttpEntity.class.isAssignableFrom(c);
        }
        return false;
    }

    @SuppressWarnings({"unchecked"})
    private <T> ResponseEntity<T> createResponse(Object instance, Response response) {

        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        for (String key : response.headers().keySet()) {
            headers.put(key, new LinkedList<>(response.headers().get(key)));
        }

        return new ResponseEntity<>((T) instance, headers, HttpStatus.valueOf(response.status()));
    }
}

使用(代码片段)

@FeignClient(value = "服务端", contextId = "OaMallFeign", fallbackFactory = OaMallFeignFallback.class, configuration = FeignGenericDecoder.class)
public interface OaMallFeign {

	 @PostMapping(value = "/feign/oa/querySku")
    <T> R<List<T>> querySku(@RequestBody @Validated SkuQueryDTO params, @RequestHeader(FeignGenericDecoder.GENERICS_HEADER) String generic);
}

@Component
public class OaMallFeignFallback implements FallbackFactory<OaMallFeign> {
	
	@Override
    public OaMallFeign create(Throwable cause) {
        log.error("OaMallFeignFallback接口调用失败", cause);

        return new OaMallFeign() {
        	@Override
            public <T> R<List<T>> querySku(SkuQueryDTO params, String generic) {
                return null;
            }
        }
     }
}

@Service
public class OaMallFeignService {

    @Autowired
    private OaMallFeign oaMallFeign;

    public <T> List<T> querySku(SkuQueryDTO params, Class<T> clz) {
        return R.get(oaMallFeign.querySku(params, clz.getName()));
    }
}

客户端调用

@Autowired
private OaMallFeignService oaMallFeignService;

public List<SkuVO> querySku() {
    SkuQueryDTO params = new SkuQueryDTO().setSkuIds(Collections.singletonList(1L)).setCompanyId(1L);

    List<SkuVO> list = oaMallFeignService.querySku(params, SkuVO.class);

    log.info("list --->>> {}", JacksonUtils.toJson(list));
    return list;
}

引用

解决方案参考资料

@FeignClient属性contextId相关资料

JSON工具类JacksonUtils

标签:feign,return,returnObject,public,import,泛型,返回值,type,response
From: https://blog.csdn.net/Khazix/article/details/139746239

相关文章

  • Android面试题之Java 泛型和Kotlin泛型
    本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点定义:JDK5引入的一种参数化类型特性继承和实现接口可以多个staticclassA{}staticinterfaceB{}staticinterfaceC{}//类必须在接口的前面staticclassD<......
  • 学习C语言两个月后的收获(篇目二) #超详细的scanf() 讲解-->基本用法、scanf() 的返回值
    一.scanf1.基本用法:scanf()读取用户的键盘输入 ---->程序在运行到这个语句的时候,会停下来,等待用户从键盘输入。当用户输入数据之后按下回车,scanf()就会处理用户的输入,将其存入变量。 scanf()是库函数,其头文件是<stdio.h>    (注:标准输入一般是键盘;标准输出......
  • SpringCloud入门之设置OpenFeign 压缩 超时时间 重试等
    文章目录前言一、为什么要配置二、配置属性1.代码2.yml配置2.1开启Feign日志2.2读取超时和连接超时2.3gzip压缩2.4变更httpclient客户端3.日志输出说明前言通过yml中设置一些属性,就可以让OpenFeign的功能更加强大,它不仅限于服务间的调用,还有请求重试、压缩......
  • 阿里巴巴中国站关键字搜索API返回值应用案例:精准定位目标用户群体
    阿里巴巴中国站的关键字搜索API返回值在精准定位目标用户群体方面,具有广泛的应用案例。这些应用案例主要集中在以下几个方面:数据分析与市场调研:通过关键字搜索API,商家可以获取大量与特定商品或服务相关的搜索数据。对这些数据进行深度分析,可以了解目标用户群体的搜索习惯......
  • 什么是Java泛型,它的优点是什么?
    什么是Java泛型?Java泛型(Generics)是一种使得类、接口和方法能够操作任意类型(类型参数化)的机制。它允许我们在编写代码时使用类型参数,从而使代码更加通用和灵活。泛型的主要目的是在编译时提供类型安全检查,并消除类型转换的需要。在Java5之前,集合类(如List、Set、Map)只能存储O......
  • 模版初阶【泛型编程】【函数模版】【类模版】
    模版初阶1.泛型编程如何实现一个通用的交换函数呢?我们先来看一个情景:假设我们需要一个交换的函数,在C语言,我们需要对每一个类型都重新编写一个不同的函数,名字也不能相同。而在c++支持重载后,虽然函数名可以相同,但是我们仍然要对每一种类型都编写一个函数。比如int类要交......
  • 泛型
    泛型就是一种自定以声明变量的类型也就是把类型变成一种参数进行传递点击查看代码publicclassa<T>{publicTX;publica(Ts){this.X=s;System.out.print(this.X);}publicstaticvoidmain(String[]args){a<String>obj......
  • SpringCloud-OpenFeign拓展-连接池、最佳使用方法、日志输出
    目录1OpenFeign连接池1.1常见连接类型1.2连接池使用方法1.2.1引入依赖1.2.2开启连接池功能1.2.3配置完成,重启实例即可,底层将更改设置。2OpenFeign最佳使用方法2.1每个微服务都是单独的project,内部有三个独立模块2.2每个微服务都是一个module,一个project,内设......
  • 泛型擦除的原理
    以下程序的输出是什么:List<String>stringList=newArrayList<>();List<Integer>intList=newArrayList<>();//输出trueSystem.out.println(stringList.class==intList.class);输出为true,这意味两个list的class地址都一样,为同一个字节码文件。这个试验也侧面反映出......
  • 通过网关实现登录验证以及微服务获取服务和OpenFeign传递用户
     网关登录校验单体架构时我们只需要完成一次用户登录、身份校验,就可以在所有业务中获取到用户信息。而微服务拆分后,每个微服务都独立部署,不再共享数据。也就意味着每个微服务都需要做登录校验,这显然不可取。鉴权思路分析我们的登录是基于JWT来实现的,校验JWT的算法复杂,而且......