Feign 踩坑指南 (接口返回泛型设置属性为null)

简介: Feign 简介Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。

777745.png

Feign 简介

Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。

Feign和OpenFeign的关系

Feign本身不支持Spring MVC的注解,它有一套自己的注解

OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。

OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,

并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

使用

依赖

<!-- 引入open-feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Feign默认所有带参数的请求都是Post,想要使用指定的提交方式需引入依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>

权限拦截器

这里针对服务与服务之间权限验证

// 定义拦截器
public class MyBasicAuthRequestInterceptor implements RequestInterceptor {
  @Override
  public void apply(RequestTemplate template) {
    // TODO Auto-generated method stub
    template.header("Authorization", "Basic cm9vdDpyb290");
  }
}
配置文件
feign:
client:
config:
service-valuation:
request-interceptors:
- com.online.taxi.passenger.feign.interceptor.MyBasicAuthRequestInterceptor

通用配置

feign:
compression:
# 配置请求GZIP压缩
request:
enabled: true
# 配置压缩支持的MIME TYPE
mime-types: text/xml,application/xml,application/json
# 配置压缩数据大小的下限
min-request-size: 2048
# 配置响应GZIP压缩
response:
enabled: true
# 采用 apache的 okhttp 作为 http访问
okhttp:
enabled: true
# feign 客户端配置
client:
config:
# 默认配置 -> 可单独指定 feignName
default:
# 链接超时时间
connectTimeout: 5000
# 读取超时时间
readTimeout: 5000
# 日志等级
loggerLevel: full

调用

脱离注册中心

@FeignClient(name = "single",url = "192.168.0.102:7601")
public interface ConsumerApiBySingle {
// 针对 Feign 的 单独调用
@GetMapping("getHi")
String getHi();
}

Eureka 调用 (Feign - Ribbon 实现的负载均衡,RestTemplate发起的调用)

@FeignClient(name = "provider")
public interface UserApi {
@GetMapping("/users/getUser")
ResultDto<User> getUser();
/**
     * 三种 传参格式
     * 1、 默认                @RequestParam
     * 2、 指定参数名           @RequestParam(name = "xxx")
     * 3、 传输一个Json格式对象  @RequestBody
     * @param userName
     * @return
     */
@PostMapping("/users/saveUser")
ResultDto<User> saveUser(@RequestParam(name = "userName") String userName);
}

踩坑

在做 SpringCloud 多服务调用时,有些人可能习惯直接使用Entity来做返回值。但是针对 熔断、降级、隔离问题时,需要做好异常状态判断就很麻烦了

于是,封装了DTO层来做服务之间的数据传输

DTO 范型大坑 ⭐️⭐️⭐️

中间数据传输层为了方便数据的传输 , 使用范型可以很好的封装起来传输对象 , 并且可以封装数据状态类型和其他信息, 坑就在这里 !!!

import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.http.HttpStatus;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
 * 统一返回参数
 *
 * @date 2020年5月15日10:40:54
 * @author Parker
 *
 * 在 Feign 的调用过程中,无法直接序列化数据
 *
 * 所以要加上 @JsonProperty ,否者返回则为一个null
 *
 */
public class ResultDto<T> implements Serializable {
  /** Map 容器 */
  private final Map<String,Object> resultMap = new HashMap<>(3);
  /** 数据 */
  private T data;
  public ResultDto(){
    resultMap.put("success", true);
    resultMap.put("code", HttpStatus.OK.value());
    resultMap.put("msg", "操作成功");
  }
  /**
   * 获得编号
   * @return
   */
  public int getCode() {
    return (int)resultMap.get("code");
  }
  /**
   * 设置编号
   * @param code
   */
  public void setCode(int code) {
    resultMap.put("code", code);
  }
  /**
   * 获得信息
   * @return
   */
  public String getMsg() {
    return (String)resultMap.get("msg");
  }
  /**
   * 设置信息
   * @param msg
   */
  public void setMsg(String msg) {//向json中添加属性,在js中访问,请调用data.msg
    resultMap.put("msg", msg);
  }
  /**
   * 获得状态
   * @return
   */
  public boolean isSuccess() {
    return (boolean)resultMap.get("success");
  }
  /**
   * 设置状态
   * @param success
   */
  public void setSuccess(boolean success) {
    resultMap.put("success", success);
  }
  // ---------------------------------
  /**
   * 设置值
   * @param value
   * @return
   */
  public ResultDto<T> put(T value) {
    data = value;
    return this;
  }
  /**
   * 获得值
   * @return
   */
  public T get(){
    return data;
  }
}

应用场景

  1. 序列化以及反序列化采用jackson
  2. 调用第三方采用feign注解式接口

问题分析

ResultDto 是一个api通用接口返回泛型类,User 为传入的具体泛型类

在Feign接口中返回泛型类时,由于Java的泛型机制,在实例化之前无法得到具体的类型 ,因此,虽然服务提供方返回的是具体实例的数据,但是在客户端decode时,无法转化为具体的类。

解决方案

针对范型的字段必须要用@JsonProperty("字段名")或者```@JsonSetter("字段名")``注解来显示声明属性名字,尤其是首字母为大写的情况,否则反序列化后的数据就为空值。

/**
 * 统一返回参数
 *
 * @date 2020年5月15日10:40:54
 * @author Parker
 *
 * 在 Feign 的调用过程中,无法直接序列化数据
 *
 * 所以要加上 @JsonProperty ,否者返回则为一个null
 *
 */
public class ResultDto<T> implements Serializable {
/** Map 容器 */
@JsonProperty("resultMap")
private final Map<String,Object> resultMap = new HashMap<>(3);
/** 数据 */
@JsonProperty("data")
private T data;
   ...
目录
相关文章
|
7月前
|
关系型数据库 MySQL 数据处理
实时计算 Flink版产品使用合集之如果在 MySQL 表中为某个字段设置了默认值,并且在插入数据时指定了该字段为 NULL,那么 MySQL 是否会使用默认值来填充这个字段
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
关系型数据库 MySQL 数据库
Flink CDC中mysql 字段设置了默认值 ,然后插入数据时 指定该字段为null 会返回字段默认值 而不是null?
Flink CDC中mysql 字段设置了默认值 ,然后插入数据时 指定该字段为null 会返回字段默认值 而不是null?
252 2
|
7月前
|
Java 数据库连接 API
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
800 0
|
7月前
|
存储 关系型数据库 MySQL
为什么建议MySQL列属性尽量NOT NULL
为什么建议MySQL列属性尽量NOT NULL
361 0
|
API
如果在云效中获取项目详情接口返回值为null
如果在云效中获取项目详情接口返回值为null
66 2
|
API
在云效中获取项目详情接口返回值为null
在云效中获取项目详情接口返回值为null
65 1
|
前端开发 Java
Java——Native Query设置null参数
Java——Native Query设置null参数
|
前端开发 小程序
钉钉小程序 上传一百张图片的base64给接口该怎么写好,正常写的话应该是前端文件限制了长度,只要太大后面一些全部为null
钉钉小程序 上传一百张图片的base64给接口该怎么写好,正常写的话应该是前端文件限制了长度,只要太大后面一些全部为null
|
存储 关系型数据库 MySQL
字段为什么要设置成 not null?
字段为什么要设置成 not null?
179 0