[Spring-Cloud-Feign] 一些问题和使用记录

前言

最近学习Sring-Cloud,遇到的一些问题,其中发现feign这个组件是遇到的坑是最多的,特此记录一下。

1.使用httpclient连接池

示例使用Apache httpclient,pom文件引入依赖

 <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
复制代码

配置文件application.yml

feign:
  name: saber-auth-provider
  httpclient:
    enabled: true
复制代码

这样就算是配置好了,验证的话我是用的idea,找到ApacheHttpClient类,在execute方法打个断点,如果请求能走进来说明配置成功。

2.Post请求Form表单支持

pom文件依赖

    <dependency>
        <groupId>io.github.openfeign.form</groupId>
        <artifactId>feign-form-spring</artifactId>
        <version>3.5.0</version>
    </dependency>
复制代码

创建Feign的配置文件

@Configuration
public class SbFeignConfiguration {
    
    /**
        spring初始化的时候创建的,不需要自己初始化这个bean
    */
    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;
    
    /**
        开启feign请求日志
    */
    @Bean
    public Logger.Level feignLoggerLevel() {
        return  Logger.Level.FULL;
    }
    
    /**
        使用formEncoder支持form表单
    */
    @Bean
    public Encoder feignFormEncoder() {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
}
复制代码

使用的时候需要注意post请求,然后参数只能用 Map<String, ?>,并且需要注意设置请求头'application/x-www-form-urlencoded' 类似这样的。

@FeignClient(value = "${feign.name}", path = "/dept")
public interface DeptCloudService {
    @PostMapping(value = "/save", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    JsonResult<Integer> saveDept(Map<String, ?> deptDto);
}
复制代码

但是这里我参数想使用POJO类而不是一个map,找了一段时间找到一个方法,就是把feign-form-spring升级到3.5.0版本.在3.5.0版本的 FormEncoder 类的 encode 方法中,贴一下源码

/**
   * 这段是3.5.0的代码
   * object 请求参数,也就是上边的map或者pojo类
   * bodyType 请求的类型
   * template 请求模板
*/
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
    String contentTypeValue = this.getContentTypeValue(template.headers());
    ContentType contentType = ContentType.of(contentTypeValue);
    if (!this.processors.containsKey(contentType)) {
        this.delegate.encode(object, bodyType, template);
    } else {
        Map data;
        if (MAP_STRING_WILDCARD.equals(bodyType)) {
            data = (Map)object;
        } else {
            /**
              *  这一段是3.5.0之前才有的,之前的版本只判断了Map类
            */
            if (!PojoUtil.isUserPojo(object)) {
                this.delegate.encode(object, bodyType, template);
                return;
            }

            data = PojoUtil.toMap(object);
        }

        Charset charset = this.getCharset(contentTypeValue);
        ((ContentProcessor)this.processors.get(contentType)).process(template, charset, data);
    }
}

/**
   * 这段是3.4.1的代码,这是3.5.0后边最近的版本了
*/
public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
    String contentTypeValue = getContentTypeValue(template.headers());
    val contentType = ContentType.of(contentTypeValue);
    //这里只对Map<String,?>进行处理
    //MAP_STRING_WILDCARD:Type literal for {@code Map<String, ?>}.
    if (!MAP_STRING_WILDCARD.equals(bodyType) || !processors.containsKey(contentType)) {
      delegate.encode(object, bodyType, template);
      return;
    }
    
    val charset = getCharset(contentTypeValue);
    val data = (Map<String, Object>) object;
    try {
      processors.get(contentType).process(template, charset, data);
    } catch (Exception ex) {
      throw new EncodeException(ex.getMessage());
    }
}
复制代码

把feign-form-spring升级到3.5.0后就可以直接使用pojo类传递参数,不过限制是3.5.0版本对应的spring-cloud版本也要升级到Greenwich.RELEASE,那对应的spring-boot又要升级,如果是正式点项目可能太大,可以考虑参考3.5.0的FormEncoder实现自己的Encoder。

3.Get请求使用pojo类

没错,又是pojo类,feign对pojo类的支持简直丧心病狂(个人认为). 一般的正常用法

@FeignClient(value = "${feign.name}", path = "/dept")
public interface DeptCloudService {
    @GetMapping("/list")
    JsonResult<PageDto> findDeptList(@QueryMap Map query);
}
复制代码

使用注解 @QueryMap 修饰一个map是没问题的,但是将Map换成pojo类就不行了,所幸前边升级了spring-cloud,新增了一个注解 @SpringQueryMap 可以用来修饰pojo类达到传递参数的需求.

@FeignClient(value = "${feign.name}", path = "/dept")
public interface DeptCloudService {
    @GetMapping("/list")
    JsonResult<PageDto> findDeptList(@SpringQueryMap DeptParam deptParam);
}
复制代码

但是,又是但是,我的pojo类里边有一些参数我希望是公共的,但是传递多个pojo类又不识别,所以我选择了继承的方式来实现,但是默认的传递方式又不能识别父属性的参数,所以又得想办法,最后翻代码执行过程和在github上的文档上乱翻,找到一个方法。

我的pojo类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptParam extends PageParam {

    private Long deptId;

    @NotBlank(message = "部门名字name不能为空")
    private String name;

    private Long parentId;

    private String remark;

    private Integer seq;
}

//我希望的公共参数,可以的话其实分两个参数传递是最好的
@Data
public class PageParam {

    /**
     * 页码,从1开始
     */
    private int pageNum = 1;

    /**
     * 页面大小
     */
    private int pageSize = 10;


    private String sortKey;

    private String sortValue;

    public String sortStr() {
        return sortStr(null);
    }

    public String sortStr(String tableAlias) {
        if (StringUtils.isBlank(tableAlias)) {
            return sortKey + " " + sortValue;
        }
        return tableAlias + "." + sortKey + " " + sortValue;
    }

}
复制代码

feign配置文件增加配置

@Configuration
public class SbFeignConfiguration {
    
    /**
        spring初始化的时候创建的,不需要自己初始化这个bean
    */
    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;
    
    /**
        开启feign请求日志
    */
    @Bean
    public Logger.Level feignLoggerLevel() {
        return  Logger.Level.FULL;
    }
    
    /**
        使用formEncoder支持form表单
    */
    @Bean
    public Encoder feignFormEncoder() {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
    
    //替换解析queryMap的类
    @Bean
    public Feign.Builder feignBuilder() {
        return Feign.builder()
                .queryMapEncoder(new BeanQueryMapEncoder())
                .retryer(Retryer.NEVER_RETRY);
    }
}
复制代码

根据代码断点走下去会发现默认的queryMap是使用 FieldQueryMapEncoder 解析的,而这个类是不会解析父类属性。BeanQueryMapEncoder是feign提供的另一个解析器,配置替换一下。

转载于:https://juejin.im/post/5c6e5e75f265da2db414225e

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值