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

问题

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

然而现实是,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

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用Feign进行接口调用时,我们可以通过统一处理返回值来简化代码。具体来说,我们可以定义一个类或接口来处理返回值。该类或接口可以使用泛型来适应不同的返回值类型。 首先,我们可以定义一个统一的接口来处理返回值,例如`ResponseProcessor`。该接口可以使用泛型来适应不同的返回值类型。我们可以在接口定义一个方法来对返回值进行处理。 ```java public interface ResponseProcessor<T> { void process(T response); } ``` 接下来,我们可以创建一个公共的处理类,例如`FeignResponseHandler`,该类实现了`ResponseProcessor`接口,并根据实际情况对返回值进行处理。例如,我们可以在`process`方法打印返回值或进行其他操作。这样,我们就可以在需要处理返回值的地方直接使用该处理类。 ```java public class FeignResponseHandler<T> implements ResponseProcessor<T> { @Override public void process(T response) { // 对返回值进行处理,例如打印返回值 System.out.println("处理返回值:" + response.toString()); } } ``` 最后,我们可以在调用Feign接口的地方使用这个处理类。在调用的时候,我们可以将处理类作为参数传入Feign的请求方法。这样,当请求返回时,就会自动调用处理类对返回值进行处理。 ```java // 调用Feign接口 FeignClient feignClient = new FeignClient(); ResponseProcessor<String> processor = new FeignResponseHandler<>(); feignClient.request(processor); // Feign接口定义 public interface FeignClient { @RequestLine("GET /api") void request(ResponseProcessor<String> processor); } ``` 这样,我们就可以将返回值的处理逻辑统一封装到一个类,并通过泛型适应不同的返回值类型。这样可以简化代码,并且使代码更具可维护性和可扩展性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值