一文教你如何使用原生的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
为什么要使用HTTP client
首先,因为 Feign 本身是一款 HTTP client,所以,这里先回答:为什么使用 HTTP client?
假设不用 HTTP client,我们访问 HTTP API 的过程大致如下。是不是相当复杂呢?直接操作 socket 已经非常麻烦了,我们还必须在熟知 HTTP 协议的前提下自行完成报文的组装和解析,代码的复杂程度可想而知。
那么,这个过程是不是可以更简单一些呢?
我们可以发现,在上面的图中,红框的部分是相对通用的,是不是可以把这些逻辑封装起来?基于这样的思考,于是就有了 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的适配包。
更换为Spring的注解
在入门例子中,我们使用 Feign 自带的注解来定义 HTTP API。但是,对于习惯了 Spring 注解的许多人来说,无疑需要增加学习成本。我们自然会问,Feign 能不能支持 Spring 注解呢?答案是肯定的。Feign 不但能支持 Spring 注解,还可以支持 JAX-RS、SOAP 等等。
下面就是使用 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 照样提供了支持。
注意,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。
这里用 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


猜你喜欢
- 读取文本文件中的内容此示例读取文本文件的内容以使用 System.IO.File 选件类的静态方法 ReadAllText 和 ReadAl
- ELK是三款软件的简称,分别是Elasticsearch、Logstash、Kibana组成,在发展的过程中,又有新成员Beats的加入,所
- 前言昨夜同门云集推杯又换盏,今朝茶凉酒寒豪言成笑谈。半生累,尽徒然,碑文完美有谁看,隐居山水之间誓与浮名散。简介今天给大家带来的是支付宝的月
- AlarmManager通常用来开发手机闹钟,并且它是一个全局定时器,可在指定时间或指定周期启动其他组件(包括Activity,Servic
- 一、实验目的(1)掌握应用黑盒测试技术进行测试用例设计。(2)掌握对测试用例进行优化设计方法。二、实验内容日期问题测试以下程序:该程序有三个
- 在一次源码查看ThreadGroup的时候,看到一段代码,为以下:/* * @throws NullPointer
- 本文实例讲述了.NET/C#实现识别用户访问设备的方法。分享给大家供大家参考,具体如下:一、需求需要获取到用户访问网站时使用的设备,根据不同
- 本文实例为大家分享了Android Camera实现可复用相机组件的具体代码,供大家参考,具体内容如下若自己的应用需要使用camera,有两
- 本文实例为大家分享了Java实现窗体程序显示日历的具体代码,供大家参考,具体内容如下实训要求:1.使用BorderLayout 进行总体布局
- 概述工作电脑用了3年多了,100G的C盘也快吃不消了,每次打开看到C盘红了,总要用清理工具清理一下子.不知道怎么最近清理工具清理
- private void btnCreate_Click(object sender, EventArgs e) ...{ int hWnd
- 本文实例为大家分享了Android日历控件的使用方法,供大家参考,具体内容如下MainActivity.java代码:package sis
- 背景最近在探秘kafka为什么如此快?其背后的秘诀又是什么?怀着好奇之心,开始像剥洋葱 一样逐层内嵌。一步步揭晓kafka能够吊打mq的真因
- Ben Evans是一名资深培训师兼顾问,他在演讲可视化垃圾回收中从基础谈起讨论了垃圾回收。以下是对其演讲的简短总结。基础当谈到释放不再使用
- 本文实例为大家分享了java图形用户界面实现菜单功能的具体代码,供大家参考,具体内容如下题目:编写一个图形用户界面,实现菜单的功能。有3个一
- 前言最近开发中用到许多对话框,之前都是在外面的代码中创建AlertDialog并设置自定义布局实现常见的对话框,诸如更新提示等含有取消和删除
- JAVAWEB dbutils执行sql命令并遍历结果集时不能查到内容的原因及处理方法如下所示:遍历结果集时只遍历bean对象才会只输出第一
- 面试题:1.如何保证多线程下 i++ 结果正确?2.一个线程如果出现了运行时异常会怎么样?3.一个线程运行时发生异常会怎样?为了避免临界区的
- 我们在构建C# Form窗口的时候经常需要到弹出新的窗口,那么接着就会如何弹出窗口的疑问。这里介绍最常见的两种弹窗方法show()和show
- 本文详细分析了C#类的访问修饰符用法,分享给大家供大家参考。具体用法分析如下:默认情况下,类声明为内部的,即只有当前工程中的代码才能访问它。