一文教你如何使用原生的Feign

作者:子月生 时间:2023-01-12 22:12:58 

目录
  • 什么是Feign

  • 为什么使用Feign

    • 为什么要使用HTTP client

    • 为什么要使用Feign

  • 如何使用Feign

    • 项目环境说明

    • 引入依赖

    • 入门例子

    • 个性化配置

      • 更换为Spring的注解

      • 自定 * 码器和编码器

      • 自定义内置的HTTP client

      • 自定义 *

      • 自定义重试器

  • 结语

    • 参考资料

      什么是Feign

      Feign 是由 Netflix 团队开发的一款基于 Java 实现的 HTTP client,借鉴了 Retrofit、 JAXRS-2.0、WebSocket 等类库。通过 Feign,我们可以像调用方法一样非常简单地访问 HTTP API。这篇博客将介绍如何使用原生的 Feign,注意,是原生的,不是经过 Spring 层层封装的 Feign。

      补充一下,在 maven 仓库中搜索 feign,我们会看到两种 Feign: OpenFeign Feign 和 Netflix Feign。它们有什么区别呢?简单地说,OpenFeign Feign 的前身就是 Netflix Feign,因为 Netflix Feign 从 2016 年开始就不维护了,所以建议还是使用 OpenFeign Feign。

      一文教你如何使用原生的Feign

      为什么使用Feign

      为什么要使用HTTP client

      首先,因为 Feign 本身是一款 HTTP client,所以,这里先回答:为什么使用 HTTP client?

      假设不用 HTTP client,我们访问 HTTP API 的过程大致如下。是不是相当复杂呢?直接操作 socket 已经非常麻烦了,我们还必须在熟知 HTTP 协议的前提下自行完成报文的组装和解析,代码的复杂程度可想而知。

      一文教你如何使用原生的Feign

      那么,这个过程是不是可以更简单一些呢?

      我们可以发现,在上面的图中,红框的部分是相对通用的,是不是可以把这些逻辑封装起来?基于这样的思考,于是就有了 HTTP client(根据类库的不同,封装的层次会有差异)。

      所以,为什么要使用 HTTP client 呢?简单地说,就是为了让我们更方便地访问 HTTP API。

      为什么要使用Feign

      HTTP client 的类库还有很多,例如 Retrofit、JDK 自带的 HttpURLConnection、Apache HttpClient、OkHttp、Spring 的 RestTemplate,等等。我很少推荐说要使用哪种具体的类库,如果真的要推荐 Feign 的话,主要是由于它优秀的扩展性(不是一般的优秀,后面的使用例子就可以看到)。

      如何使用Feign

      关于如何使用 Feign,官方给出了非常详细的文档,在我看过的第三方类库中,算是比较少见的。

      本文用到的例子也是参考了官方文档。

      项目环境说明

      os:win 10

      jdk:1.8.0_231

      maven:3.6.3

      IDE:Spring Tool Suite 4.6.1.RELEASE

      引入依赖

      这里引入 gson,是因为入门例子需要有一个 json 解码器。


         <properties>
             <feign.version>11.2</feign.version>
         </properties>

      <dependencies>
             <dependency>
                 <groupId>io.github.openfeign</groupId>
                 <artifactId>feign-core</artifactId>
                 <version>${feign.version}</version>
             </dependency>
             <dependency>
                 <groupId>io.github.openfeign</groupId>
                 <artifactId>feign-gson</artifactId>
                 <version>${feign.version}</version>
             </dependency>
         </dependencies>

      入门例子

      入门例子中使用 Feign 来访问 github 的接口获取 Feign 这个仓库的所有贡献者。

      通过下面的代码可以发现,Feign 本质上是使用了 * 来生成访问 HTTP API 的代码,定义 HTTP API 的过程有点像在定义 advice。


      // 定义HTTP API
      interface GitHub {

      @RequestLine("GET /repos/{owner}/{repo}/contributors")
         // @RequestLine(value = "GET /repos/{owner}/{repo}/contributors", decodeSlash = false)// 测试转义"/"、"+"
         // @RequestLine("GET /repos/{owner:[a-zA-Z]*}/{repo}/contributors")// 测试正则校验
         // @Headers("Accept: application/json") // 测试添加header
         List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
      }

      public static class Contributor {
         String login;
         int contributions;
      }

      public class MyApp {

      public static void main(String... args) {
             // 获取用来访问HTTP API的代理类
             GitHub github = Feign.builder()
                     .decoder(new GsonDecoder()) // 返回内容为json格式,所以需要用到json解码器
                     // .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true)) // 配置超时参数等
                     .target(GitHub.class, "https://api.github.com");

      // 像调用方法一样访问HTTP API
             github.contributors("OpenFeign", "feign").stream()
                 .map(contributor -> contributor.login + " (" + contributor.contributions + ")")
                 .forEach(System.out::println);
         }
      }

      个性化配置

      除了简单方便之外,Feign 还有一个很大的亮点,就是有相当优秀的扩展性,几乎什么都可以自定义。下面是官方给的一张图,基本涵盖了 Feign 可以扩展的内容。每个扩展支持都有一个对应的适配包,例如,更换解码器为 jackson 时,需要引入io.github.openfeign:feign-jackson的适配包。

      一文教你如何使用原生的Feign

      更换为Spring的注解

      在入门例子中,我们使用 Feign 自带的注解来定义 HTTP API。但是,对于习惯了 Spring 注解的许多人来说,无疑需要增加学习成本。我们自然会问,Feign 能不能支持 Spring 注解呢?答案是肯定的。Feign 不但能支持 Spring 注解,还可以支持 JAX-RS、SOAP 等等。

      一文教你如何使用原生的Feign

      下面就是使用 Sping 注解定义 HTTP API 的例子。注意,pom 文件中要引入 io.github.openfeign:feign-spring4 的依赖。


      // 定义HTTP API
      interface GitHub {

      @GetMapping("/repos/{owner}/{repo}/contributors")
         List<Contributor> contributors(@RequestParam("owner") String owner, @RequestParam("repo") String repo);
      }

      public class MyApp {

      public static void main(String... args) {
             // 获取用来访问HTTP API的代理类
             GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .contract(new SpringContract())// 自定义contract
                     .target(GitHub.class, "https://api.github.com");
         }
      }

      自定 * 码器和编码器

      在入门例子中,我们使用 gson 来解析 json。那么,如果我想把它换成 jackson 行不行?Feign 照样提供了支持。

      一文教你如何使用原生的Feign

      注意,pom 文件中要引入 io.github.openfeign:feign-jackson 的依赖。


      public class MyApp {

      public static void main(String... args) {
             // 获取用来访问HTTP API的代理类
             GitHub github = Feign.builder()
                     .decoder(new JacksonDecoder()) // 自定 * 码器
                     .encoder(new JacksonEncoder()) // 自定义编码器
                     .target(GitHub.class, "https://api.github.com");
         }
      }

      自定义内置的HTTP client

      接下来的这个自定义就更厉害了。Feign 本身作为一款 HTTP client,竟然还可以支持其他 HTTP client。

      一文教你如何使用原生的Feign

      这里用 OkHttp 作例子。注意,pom 文件中要引入 io.github.openfeign:feign-okhttp 的依赖。


      public class MyApp {

      public static void main(String... args) {
             // 获取用来访问HTTP API的代理类
             GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .client(new OkHttpClient())// 自定义client
                     .target(GitHub.class, "https://api.github.com");
         }
      }

      自定义 *

      我们访问外部接口时,有时需要带上一些特定的 header,例如,应用标识、token,我们可以通过两种方式实现:一是使用注解定义 HTTP API,二是使用 * (更常用)。下面的例子中,使用 * 给请求添加 token 请求头。


      public class MyInterceptor implements RequestInterceptor {

      @Override
         public void apply(RequestTemplate template) {
             template.header("token", LoginUtils.getCurrentToken());
         }
      }
      public class MyApp {

      public static void main(String... args) {
             // 获取用来访问HTTP API的代理类
             GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .requestInterceptor(new MyInterceptor())
                     .target(GitHub.class, "https://api.github.com");
         }
      }

      自定义重试器

      默认情况下,Feign 访问 HTTP API 时,如果抛出IOException,它会认为是短暂的网络异常而发起重试,这时,Feign 会使用默认的重试器feign.Retryer.Default(最多重试 5 次),如果不想启用重试,则可以选择另一个重试器feign.Retryer.NEVER_RETRY。当然,我们也可以自定义。

      奇怪的是,Feign 通过重试器的 continueOrPropagate(RetryableException e)方法是否抛出RetryableException来判断是否执行重试,为什么不使用 true 或 false 来判断呢?

      注意,重试器是用来判断是否执行重试,自身不包含重试的逻辑。


      public class MyRetryer implements Retryer {

      int attempt = 0;

      @Override
         public void continueOrPropagate(RetryableException e) {
             // 如果把RetryableException抛出,则不会继续重试
             // 否则继续重试
             if(attempt++ >= 3) {// 重试三次
                 throw e;
             }
         }

      @Override
         public Retryer clone() {
             return this;
         }
      }
      public class MyApp {

      public static void main(String... args) {
             // 获取用来访问HTTP API的代理类
             GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .retryer(new MyRetryer())
                     //.retryer(Retryer.NEVER_RETRY) // 不重试
                     .exceptionPropagationPolicy(ExceptionPropagationPolicy.UNWRAP)
                     .target(GitHub.class, "https://api.github.com");
         }
      }

      结语

      以上,基本讲完 Feign 的使用方法,其实 Feign 还有其他可以扩展的东西,例如,断路器、监控等等。感兴趣的话,可以自行分析。

      参考资料

      Feign github

      相关源码请移步:https://github.com/ZhangZiSheng001/feign-demo

      来源:https://www.cnblogs.com/ZhangZiSheng001/p/14989165.html

      标签:原生,feign
      0
      投稿

      猜你喜欢

    • Android RecyclerView网格布局示例解析

      2023-03-04 00:49:44
    • 通俗易懂的C#之反射教程

      2021-08-08 19:35:02
    • C#中异步和多线程的区别介绍

      2021-08-23 07:06:49
    • Android实现极简打开摄像头

      2022-09-10 15:56:51
    • DataGridView带图标的单元格实现代码

      2021-08-07 16:50:04
    • Android编程实现TextView部分颜色变动的方法

      2021-05-29 15:12:23
    • C#模拟Http与Https请求框架类实例

      2023-02-10 16:28:11
    • Java程序图形用户界面设计之按钮与布局

      2023-07-18 07:03:21
    • android,不显示标题的方法小例子

      2023-07-10 17:14:36
    • 一文详解Java Netty中的Constant类

      2023-03-03 17:17:55
    • SpringBoot集成Jpa对数据进行排序、分页、条件查询和过滤操作

      2022-03-06 19:17:50
    • C#集合本质之队列的用法详解

      2023-03-17 06:42:38
    • Android实现视图轮播效果

      2023-04-14 08:52:10
    • Java Runnable线程传参,实现让run访问参数

      2023-09-04 21:36:42
    • OpenCV画任意圆弧曲线

      2023-06-22 19:28:44
    • Java基于Calendar类输出指定年份和月份的日历代码实例

      2023-09-20 07:40:57
    • java向文件中追加内容与读写文件内容源码实例代码

      2021-11-15 11:45:13
    • python gstreamer实现视频快进/快退/循环播放功能

      2022-07-06 10:09:20
    • C#中ref关键字的用法

      2022-07-17 21:30:47
    • C++实现企业职工工资管理系统

      2023-11-02 17:34:34
    • asp之家 软件编程 m.aspxhome.com