范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

SpringCloudFeign如何使用对象参数

  概述
  Spring Cloud Feign 用于微服务的封装,通过接口代理的实现方式让微服务调用变得简单,让微服务的使用上如同本地服务。但是它在传参方面不是很完美。在使用 Feign 代理 GET 请求时,对于简单参数(基本类型、包装器、字符串)的使用上没有困难,但是在使用对象传参时却无法自动的将对象包含的字段解析出来。  如果你没耐心看完,直接跳到最后一个标题跟着操作就行了。@RequestBody
  对象传参是很常见的操作,虽然可以通过一个个参数传递来替代,但是那样就太麻烦了,所以必须解决这个问题。
  我在网上看到有人用 @RequestBody 来注解对象参数,我在尝试后发现确实可用。这个方案实际使用 body 体装了参数(使用的是 GET 请求),但是这个方案有些问题:  注解需要在 consumer 和 provider 两边都有,这造成了麻烦  使用接口测试工具 Postman 无法跑通微服务,后来发现是因为 body 体的格式选择不正确,这个格式不是通常的表单或者路径拼接,而是 GraphQL。我没有研究过这种格式应该如何填写参数,但是 Postman 上并没有给出像表单那样方便的格式,这对于测试是很不利的。  @SpringQueryMap
  于是我继续寻找答案,发现可以使用 @SpringQueryMap 仅添加在 consumer 的参数上就能自动对 Map 类型参数编码再拼接到 URL 上。而我用的高版本的 Feign,可以直接把对象编码。
  可是正当我以为得到正解时,却发现还是有问题:
  我明明在 Date 类型的字段上加上了   @DateTimeFormat(pattern = "yyyy-MM-dd")   ,却没有生效,他用自己的方式进行了编码(或者说序列化),而且官方确实没有提供这种格式化方式。
  又一番找寻后发现了一位大佬自己实现了一个注解转换替代 @SpringQueryMap,并实现了丰富的格式化功能 ORZ(原文链接: Spring Cloud Feign实现自定义复杂对象传参 ),只能说佩服佩服。但是我没有那样的技术,又不太想复制粘贴他那一大堆的代码,因为出了问题也不好改,所以我还是想坚持最大限度地使用框架,最小限度的给框架填坑。  QueryMapEncoder
  终于功夫不费有心人,我发现了 Feign 预留的自定义编码器接口 QueryMapEncoder,框架提供了两个实现:  FieldQueryMapEncoder  BeanQueryMapEncoder
  虽然这两个实现不能满足我的要求,但是只要稍加修改写一个自己的实现类就行了,于是我在 FieldQueryMapEncoder 的基础上修改,仅仅添加了一个方法,小改了一个方法就实现了功能。
  原理:Feign 其实还是用   Map   进行的编码,编码方式也很简单,String 是 key,Object 是 value。最开始的方式就是用 Object 的 toString() 方法把参数编码,这也是为什么 Date 字段会变成一个默认的时间格式,因为 toString() 根本和 @DateTimeFormat 没有关系。而高版本使用编码器实现了对象传参,实际实际上是通过简单的反射获取对象的元数据,再放到 Map 中。
  上面的原理都能从 @DateTimeFormat 的注释和编码器的源码中得到答案。
  我们要做的就是自定义一个编码器,实现在元数据放入 Map 之前根据需要把字段变成我们想要的字符串。下面是我实现的代码,供参考:  package com.example.billmanagerfront.config.encoder;  import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors;  import org.springframework.format.annotation.DateTimeFormat;  import feign.Param; import feign.QueryMapEncoder; import feign.codec.EncodeException;  public class PowerfulQueryMapEncoder implements QueryMapEncoder {     private final Map, ObjectParamMetadata> classToMetadata = new ConcurrentHashMap<>();      @Override     public Map encode(Object object) throws EncodeException {         ObjectParamMetadata metadata = classToMetadata.computeIfAbsent(object.getClass(),                 ObjectParamMetadata::parseObjectType);          return metadata.objectFields.stream()                 .map(field -> this.FieldValuePair(object, field))                 .filter(fieldObjectPair -> fieldObjectPair.right.isPresent())                 .collect(Collectors.toMap(this::fieldName, this::fieldObject));      }      private String fieldName(Pair> pair) {         Param alias = pair.left.getAnnotation(Param.class);         return alias != null ? alias.value() : pair.left.getName();     }      // 可扩展为策略模式,支持更多的格式转换     private Object fieldObject(Pair> pair) {         Object fieldObject = pair.right.get();         DateTimeFormat dateTimeFormat = pair.left.getAnnotation(DateTimeFormat.class);         if (dateTimeFormat != null) {             DateFormat format = new SimpleDateFormat(dateTimeFormat.pattern());             format.setTimeZone(TimeZone.getTimeZone("GMT+8")); // TODO: 最好不要写死时区             fieldObject = format.format(fieldObject);         } else {          }         return fieldObject;     }      private Pair> FieldValuePair(Object object, Field field) {         try {             return Pair.pair(field, Optional.ofNullable(field.get(object)));         } catch (IllegalAccessException e) {             throw new EncodeException("Failure encoding object into query map", e);         }     }      private static class ObjectParamMetadata {          private final List objectFields;          private ObjectParamMetadata(List objectFields) {             this.objectFields = Collections.unmodifiableList(objectFields);         }          private static ObjectParamMetadata parseObjectType(Class<?> type) {             List allFields = new ArrayList();              for (Class<?> currentClass = type; currentClass != null; currentClass = currentClass.getSuperclass()) {                 Collections.addAll(allFields, currentClass.getDeclaredFields());             }              return new ObjectParamMetadata(allFields.stream()                     .filter(field -> !field.isSynthetic())                     .peek(field -> field.setAccessible(true))                     .collect(Collectors.toList()));         }     }      private static class Pair {         private Pair(T left, U right) {             this.right = right;             this.left = left;         }          public final T left;         public final U right;          public static  Pair pair(T left, U right) {             return new Pair<>(left, right);         }      } }
  加注释的方法,就是我后添加进去的。encode 方法的最后一行稍微修改了一下,引用了我加的方法,其他都是直接借鉴过来的(本来我想更偷懒,直接继承一下子,但是它用了私有的内部类导致我只能全部复制粘贴了)。  解决方案不用引入其他的 Feign 依赖,保证有下面这个就行(看网上其他方法还要引入特定依赖,要对应版本号,挺麻烦的)       org.springframework.cloud     spring-cloud-starter-openfeign 编写上面那样的类,你可以直接复制过去改个包名就行,如果还需要除了 Date 以外的格式化,请看注释和文章分析。其中我对日期的格式化,直接使用了 @DateTimeFormat 提供的模式,和 Spring 保持了一致。  编写一个 Feign 配置类,将刚自定义的编码器注册进去。细节我就不多说了:  package com.example.billmanagerfront.config;  import com.example.billmanagerfront.config.encoder.PowerfulQueryMapEncoder;  import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  import feign.Feign; import feign.Retryer;  @Configuration public class FeignConfig {     @Bean     public Feign.Builder feignBuilder() {         return Feign.builder()                 .queryMapEncoder(new PowerfulQueryMapEncoder())                 .retryer(Retryer.NEVER_RETRY);     } }Feign 代理接口中声明使用这个配置类,细节不谈  package com.example.billmanagerfront.client;  import java.util.List;  import com.example.billmanagerfront.config.FeignConfig; import com.example.billmanagerfront.pojo.Bill; import com.example.billmanagerfront.pojo.BillType;  import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.SpringQueryMap; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;  @FeignClient(name = "BILL-MANAGER", path = "bill", configuration = FeignConfig.class) public interface BillClient {     @GetMapping("list")     List list(@SpringQueryMap(true) Bill b);      @GetMapping("type")     List type();      @DeleteMapping("delete/{id}")     public String delete(@PathVariable("id") Long id); }

评测顶级商务本再进化ThinkPadX1Carbon2021ThinkPadX1Carbon2021升级到了1610比例的屏幕,提升了屏占比,同时内部的设计也有进一步的优化,延续一直优秀的键盘手感和非常安静的风扇,出色的续航以及军标对照测试体验5月27日发布的OPPOReno6有哪些亮点和槽点夏日晴海的Reno6配合非常自然地视频美妆视频虚化,还有最新一代最高骁龙870的升级,会是这个夏天非常适合轻薄手机的组合Reno6系列如期而至,还是这个一眼就能认出来的精致设计,以体验iQOONeo5快速上手屏幕由144Hz高刷换成了120Hz,但素质提升,配备振感线性马达,双扬HiRes认证。iQOONeo5在同价位之中有很好的屏幕,优秀的散热和性能,以及更好支持游戏的独立显示芯片。美的智能大会华为鸿蒙智能家居操作系统成亮点美的智能大会华为鸿蒙智能家居操作系统成亮点根据权威调研机构IDC最新数据显示2021年,虚拟现实可穿戴设备与智能家居这三类产品的总价值预计将达3721亿美元,到2025年规模将达5懂行中国行2021华为榆林碳中和能源峰会成功举办绽放矿山的智慧之花懂行中国行2021华为榆林碳中和能源峰会成功举办榆林市人民政府副市长杨向喜10月13日,以智慧能源,榆见新机为主题,懂行中国行2021华为榆林碳中和能源峰会在素有华为郭振兴只有煤矿工业互联网才能彻底实现矿山智能化华为郭振兴只有煤矿工业互联网才能彻底实现矿山智能化华为煤矿军团副总裁郭振兴2021年是国家十四五开局之年,国家大力推进产业数字化战略的转型。煤炭作为我国国民经济的重要组成部分,一直华为大消息!任正非亲自督战,成立四大军团华为大消息!任正非亲自督战,成立四大军团!研发投入太牛了,远超BAT总和华为又有大消息!证券时报e公司记者最新获悉,华为于近日正式发文成立海关和港口智慧公路数据中心能源和智能光伏四报告华为P50Pro屏幕素质报告这是P50Pro,华为目前最新的影像旗舰,它利用全新的原色引擎和计算光学,让4摄组合超常发挥,因此屏幕的硬件和HarmonyOS2上的色彩管理也变得非常重要,这就是我们对P50Pr内存小且浏览速度极快!手机新型搜索工具,你值得拥有羊搜搜是一款便捷小巧,功能超多而且超级实用的小工具,支持的资源很丰富,喜欢看小说的,喜欢磁力搜索的,喜欢看视频的,喜欢听免费音乐的,以及喜欢看漫画的,小羊搜搜都可以满足你的需求,同你喜欢不如我喜欢TFZNo。3林氏评测我还就不信了,所有人都那么喜欢真无线?真无线的优点我们都清楚没有线嘛,你耳朵就显得特没束缚,而且作为当红炸子鸡它在飞速进化着,再过几个月带降噪的真无线应该会大面积铺货,暂时没人抢得与众不同的风TANCHJIM浅野天琪林氏评测这世上的事,自己趟出一条路比跟着别人的脚印难一些。在趟这条路的开始,你最好想清楚自己为什么要这么做,这中间,势必会遇到相对更多的困难和风险,你得有坚持下去的勇气,有不动摇的恒心,这
阿里调整组织架构淘宝和天猫两大业务将在后台实现全面融合新京报贝壳财经讯(记者程子姣)1月6日,阿里巴巴集团中国数字商业板块分管总裁戴珊发布内部信,宣布原淘宝天猫业务的新组织架构。其中,最受关注的调整是,在淘宝天猫双品牌运营的基础上,新雷军晒小米12Pro蒙特里安保护壳请设计专门制作,不量产IT之家1月6日消息,今天小米创始人兼董事长雷军在微博晒出了一款比较特别的小米12Pro保护壳,号称是请设计同学制作的。这款保护壳名为蒙特里安保护壳,据雷军称,由于销售同学觉得需求反击开始了?国产厂商一周发布三款新机,iPhone13迎来最强挑战得益于iPhone13系列机型的热销,在2021年10月份及11月份,苹果成功拿下了国内市场的销量冠军,所有国产手机厂商都不是它的对手。这充分证明了一个事实尽管现在大多数国人嘴上都Java练习求圆的周长面积,交换数组中的最大值和最小值求圆的周长面积importjava。util。ScannerpublicclassTestCirclepublicstaticvoidmain(Stringargs)Scanner越狱商店Cydia创建者起诉苹果,遭法官驳回IT之家1月6日消息,美国地区法官YvonneGonzalezRogers今天批准了苹果公司的动议,驳回了JayFreeman对该公司提起的诉讼。JayFreeman是知名越狱商店98英寸电视TCL销量登顶!盘点2021火爆市场的巨幕电视同一个尺寸的电视产品往往会被归为一类。而在一类产品中,产品质量与价格往往会直接决定销量,因此销量排名也成为了许多人购买产品的重要参考。98英寸巨幕电视作为一个新兴物种,无疑是近两年田言梦语英伟达投资69亿美元只为争夺台积电5nm产能RTX40系显卡将采用AdaLovelaceGPU架构,基于5nm制程工艺,核心加速频率提升至2。22。5GHz。至于显卡核心不会采用MCM多芯片封装,所以说依旧是单芯片设计。令人碰到这几样产品男人也难做到理性消费心里总想买点啥?看看必买,全网最有料的场景种草指南。都说男人是属于理性消费群体,在商家的营销手册中,男人的消费价值甚至要排在宠物的后面,但我想说还有一句话叫做男人至死是少年作为一个清华大学国强研究院杯全球人工智能与机器人双创大赛决赛在顺德举行6日,第二届清华大学国强研究院杯全球人工智能与机器人双创大赛决赛暨颁奖典礼在顺德博智林总部举行。本次大赛由清华大学国强研究院主办,清华大学人工智能研究院广东省国强公益基金博智林机器新能源汽车在三四线城市更靠谱看到标题,你是否会惊讶,对的,新能源汽车在小城市确实比大城市要吃香,我有一辆1。4T的六代高尔夫,一辆九代雅阁,一辆小鹏P7,这三辆车是依次购买的,代表我每个年龄段的用车需求,我不2022年CES展会,针对特斯拉的江湖追杀令新内容这段时间,2022年的CES展览会已经英国拉斯维加举办。出不来预料的是,此次会展的主人公是电动车。照理说CES的中文翻译是通讯产品展览会,车辆与电子设备或是有一些差异的。而现