【Spring Cloud 进阶】OpenFeign 底层原理解析

参考文章

  • 万字+33张图探秘OpenFeign核心架构原理 | 三友
  • SpringCloud OpenFeign源码详细解析
  • Java 代理机制

OpenFeign 是一个精彩的使用动态代理技术的典型案例,通过分析其底层实现原理,我们可以对动态代理技术有进一步的理解。

目录

    • 1. Feign 与 OpenFeign
      • 1.1 OpenFeign 的使用
      • 1.2 Feign 的原生使用
    • 2. Feign 生成代理实例的原理
      • 2.1 Feign 大致原理
      • 2.2 Feign 的核心组件一览
      • 2.3 动态代理生成原理
        • 2.3.1 Feign 的 Builder
        • 2.3.2 Feign Builder 的 target 函数
        • 2.3.3 build 出来的 Feign 实例是什么
        • 2.3.4 ReflectiveFeign 是如何创建出代理类的
        • 2.3.5 InvocationHandler 的 invoke 方法
        • 2.3.6 总结动态代理的生成逻辑 ⭐⭐⭐⭐⭐
    • 3. Feign 的一次 HTTP 调用执行的过程
    • 4. 总结

1. Feign 与 OpenFeign

Feign 由 Netflix 开源,Spring Cloud 对 Feign 进行了封装整合从而形成了 OpenFeign 项目。所以这篇文章之后的内容不再可以区分 Feign 和 OpenFeign。

在 Spring Cloud 项目中,以下代码可以引入 OpenFeign 依赖:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

1.1 OpenFeign 的使用

在 Spring Cloud 中使用 OpenFeign 还是很简单的,可以参考 Feign 远程调用

1.2 Feign 的原生使用

OpenFeign 是 Spring Cloud 官方对 Feign 封装整合之后的用法,原生 Feign 的用法有些区别。

加入我们想通过 Feign 来调用 https://tenapi.cn/v2/yiyan 这个公开接口,我们可以这样做:

  • 首先声明一个 TenAPIClient 的 interface:
public interface TenAPIClient {@RequestLine("GET /v2/yiyan")String yiYan();
}
  • 使用:
public class OrderFeignDemo {public static void main(String[] args) {TenAPIClient tenAPIClient = Feign.builder().target(TenAPIClient.class, "https://tenapi.cn");String s = tenAPIClient.yiYan();System.out.println(s);}
}

在上面例子中,我们可以看到,我们只需要声明一个 TenAPIClient 的 interface,以及 API 的调用函数声明,Feign 就可以通过动态代理技术生成一个实现了能够发起远程调用这个 API 的 client 实例。这就是 Feign 对动态代理技术的妙用。

2. Feign 生成代理实例的原理

这里的源码基于 Spring Boot 3.2.3,Spring Cloud 2023.0.0

Feign 根据我们编写的 interface 动态生成一个实现了这个 interface 的代理实例,我们的代码就是通过这个代理实例实现了真正的远程调用 API 的逻辑。这一节就看看 Feign 是如何生成这个代理实例的。

2.1 Feign 大致原理

下图展示了 Feign 框架的原理,它根据 interface 使用动态代理生成代理类,我们使用代理类发起了 HTTP 请求并得到响应结果。
Feign 大致原理

2.2 Feign 的核心组件一览

在我们的 main 代码中的 Feign.builder() .target(TenAPIClient.class, "https://tenapi.cn"); 用来生成代理类,这里的 builder 中的成员就是 Feign 的各个核心组件了,我们来看一下:

Builder
在这里插入图片描述
简要介绍一下各个组件的作用:

  • Client:一个 HTTP Client 的接口,具体实现可以是 JDK 内置的 HttpURLConnection,也可以是 Apache HttpClient 或 OkHttp,Feign 使用这个 Client 来发送 HTTP 请求。
  • Contract:解析 interface 中方法的注解和参数,用于得知各个 API 的相关信息。这个组件在代码中通过解析各个方法的注解和参数(比如 @RequestLine("GET /v2/yiyan")),获得 List<MethodMetadata>,即各个 method 的 meta data,比如 URL、body、header 等等信息。
  • Retryer:用于实现重试的逻辑
  • RequestInterceptorResponseInterceptor:是一个拦截接口,通过这个接口,可以实现在 Http 请求发送之前或接收到响应之后对内容进行修改。
  • EncoderDecoder:用于序列化请求和反序列化响应的接口
  • InvocationHandlerFactory:用于创建 InvocationHandler,我们都知道,JDK 动态代理就是通过 InvocationHandler 来生成代理实例的,所以 InvocationHandler 的 invoke() 方法的逻辑就是动态代理走的核心逻辑。

2.3 动态代理生成原理

关于如何通过动态代理来获得 TenAPIClient 的实现类,其实就是分析 Feign.builder().target(TenAPIClient.class, "https://tenapi.cn"); 这一行代码在内部做了什么工作。

Feign 这个类是什么呢?在源码中有这样一行注释:

In implementation, Feign is a factory for generating targeted http apis.

所以,Feign 就是一个用来创建 TenAPIClient 实例的 factory。

2.3.1 Feign 的 Builder

Feign.builder() 会创建一个 Builder 类,用来创建 Feign,这个 Builder 类描述了 Feign 的各个关键组件,即 2.2 节介绍的那些,比如 Contract、Client、Retryer 等等,当我们需要 Feign 满足一些我们特殊需求时,我们可以通过 Builder 来替换个别关键组件,比如替换 HTTP Client 的实现、替换 Retryer 来更换重试策略等。在 Spring Cloud 整合 Feign 时,就通过这里来替换了一些关键组件,比如替换了重试策略等。

下面看一下 Feign.builder().target(TenAPIClient.class, "https://tenapi.cn"); 中 target 所做的事情:

target 实现
我们调用的是第一个 target 函数,传入了 TenAPIClient.class 和 baseURL,这样他就生成了一个 TenAPIClient 的代理实现类。

2.3.2 Feign Builder 的 target 函数

在第一个 target 函数中,他使用 apiType 和 url 初始化了一个 HardCodedTarget 类实例,HardCodedTarget 类实现了 Target 接口,这个接口是用来将一个 RequestTemplate 实例转换为一个真正的 Request 实例的。

第一个 target 函数调用了第二个 target 函数,第二个 target 函数就复杂了,它先 build() 创建了一个 Feign 实例,我们知道 Feign 就是一个 factory,这个 factory 创建出代理类,就如图中 target 函数的实现一样,build 出一个 Feign 实例后,就通过 newInstance() 创建出一个代理类。

所以现在来看看 build() 创建出的 Feign 实例是什么,以及 Feign 实例如何创建出代理类的。

2.3.3 build 出来的 Feign 实例是什么

build() 函数最终通过如下函数来构建出 Feign 实例:

build函数

这里有两个需要注意的点:

  • 我们生成的 Feign 实例是 ReflectiveFeign 的类实例(因为 Feign 只是一个抽象类)
  • 我们有了一个 SynchronousMethodHandler 的 factory,它用来生成 MethodHandler。MethodHandler 是用来实现代理类中各个方法调用的逻辑的,比如我们在 TenAPIClient 中声明的 yiYan() 方法的具体逻辑就是由一个 MethodHandler 来实现的。

既然我们知道了 Feign 的实现类就是 ReflectiveFeign,那我们就仔细看一下 ReflectiveFeign 的代码。

2.3.4 ReflectiveFeign 是如何创建出代理类的

这个问题的代码都在 ReflectiveFeign 的 newInstance() 方法中,我们来看一下:

newInstance

上图中被红色方框圈中的代码是核心代码,我们重点看这一部分。

其中,methodToHandler 是一个从 Method 到 MethodHandler 的 map,Method 也就是我们在 TenAPIClient 接口中定义的方法,MethodHandler 我们之前提到了,就是用来处理这个方法的具体逻辑,即包含真正执行远程调用的逻辑。targetToHandlersByName.apply(target, requestContext); 创建出这个 map 的原理大概就是,通过 Contract 解析出各个方法的 MethodMetaData,然后根据 Target、MethodMetaData 创建出实现了这个远程调用逻辑的 MethodHandler,从而构建出这个 map,这部分代码如下:

在这里插入图片描述
然后,我们接着看前面 newInstance 的核心代码,在得到 Method -> MethodHandler 的 map 后,接着创建出 InvocationHandler,学过 JDK 动态代理的都知道,InvocationHandler 是用来创建出最终的代理类的东西,这部分忘了的可以看一下 Java 代理机制 的 JDK 动态代理部分。

这里的 Proxy、InvocationHandler 都是 JDK 定义的,也是 JDK 实现的动态代理技术。所以 Feign 只需要按照 JDK 的要求去实现出自己的 InvocationHandler 实现,就能创建出一个实现了指定接口的动态代理类实例。

InvocationHandler 实现了代理的逻辑,当我们在 main 中写出 tenAPIClient.yiYan(); 时,其实就是调用了 InvocationHandler 实例的 invoke() 方法,这里的 invoke 方法就实现了向 API 发送 HTTP 请求并接收响应的逻辑。

在得到 InvocationHandler 的实例后,就通过 Proxy.newProxyInstance() 并传入 classLoader、所需要代理的类(在我们例子中就是 TenAPIClient)和 InvocationHandler,然后 JDK 动态代理就生成了一个代理类,也就是我们 main demo 中的 tenAPIClient 变量实例。

当我们调用 tenAPIClient 的 yiYan() 方法时,它其实就是调用了 InvocationHandler 的 invoke 方法,所以我们需要看一下这里 invoke 方法的实现是什么,它是如何实现了向 API 发送 HTTP 请求的。

2.3.5 InvocationHandler 的 invoke 方法

这里所说的 InvocationHandler 的实现类是 FeignInvocationHandler

FeignInvocationHandler

可以看到 invoke 的实现很简单,关键在于 return 后面这一行,就是根据你调用的函数是什么,找到相应的 MethodHandler,然后调用 MethodHandler 的 invoke 方法,执行 MethodHandler 中存放的远程调用的代码逻辑。比如 tenAPIClient.yiYan(); 这个调用就是调用的 FeignInvocationHandler 的 invoke 方法,invoke 方法知道你调用的方法名是 yiYan,然后找到这个 method handler,然后 method handler 执行其远程调用的逻辑,实现最终的远程调用。

2.3.6 总结动态代理的生成逻辑 ⭐⭐⭐⭐⭐

在这里插入图片描述
总结如下:我们编写出 TenAPIClient 的接口和接口方法,Feign 通过 Contract 解析这个接口和其中的方法,得到各方法的 MethodMetaData,然后由此创建出各方法的 MethodHandler,再利用这些 method handlers 实现出 InvocationHandler,使用这个 InvocationHandler 利用 JDK 动态代理技术创建出动态代理对象。

3. Feign 的一次 HTTP 调用执行的过程

前面介绍原理时,也介绍的差不多了。当我们调用接口的方法时,动态代理技术会交由 InvocationHandler 的 invoke 方法来处理,invoke 方法根据你调用的方法名找到相应的 method handler 并执行相应的远程调用逻辑。用图示来解释:

在这里插入图片描述
以上就是 Feign 一次 HTTP 调用的执行过程。

4. 总结

以上介绍了 Netflix 开源的 Feign 的实现原理,它精彩地运用了动态代理技术,实现了只需要我们写出接口方法,Feign 就生成了具体的远程调用逻辑,而我们只需要关注远程调用的 API 相关参数即可。

本篇文章未涉及 Spring Cloud 如何整合 Feign,关于整合 Feign 的部分在之后再深入研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/266442.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全攻防演练:企业蓝队建设指南

第一章 概述 背景 网络实战攻防演习是当前国家、重要机关、企业组织用来检验网络安全防御能力的重要手段之一,是对当下关键信息系统基础设施网络安全保护工作的重要组成部分。网络攻防实战演习通常是以实际运行的信息系统为攻击目标,通过在一定规则限定下的实战攻防对抗,最…

anaconda 安装环境出现 DEBUG:urllib3.connectionpool:Starting new HTT

在anaconda中向安装一个新环境&#xff0c;发现以下报错&#xff0c;虽然他会继续出现环境安装的y/n&#xff0c;但安装之后&#xff0c;后续的一些包的安装仍然有问题。 参照了网上的一些做法&#xff0c;为选择更新conda-build&#xff1a; conda install -c conda-forge c…

npm使用国内淘宝镜像的方法整理

命令配置安装&#xff1a; 淘宝镜像&#xff1a; npm config set registry https://registry.npm.taobao.org/ 官方镜像&#xff1a; npm config set registry https://registry.npmjs.org 通过cnpm安装&#xff1a; npm install -g cnpm --registryhttps://registry.npm.…

RFID射频识别技术的优势

目前RFID在金融支付、物流、零售、制造业、医疗、身份识别、防伪、资产管理、交通、食品、动物识别、汽车、等行业都已经实现不同程度的商业化使用。未来&#xff0c;RFID技术有不可替代的六大优势&#xff0c;也保证了物联网的万物互联的有序发展! 1、无需可视&#xff0c;在无…

状态码转文字!!!(表格数字转文字)

1、应用场景&#xff1a;在我们的数据库表中经常会有status这个字段&#xff0c;这个字段经常表示此类商品的状态&#xff0c;例如&#xff1a;0->删除&#xff0c;1->上架&#xff0c;0->下架&#xff0c;等等。 2、我们返回给前端数据时&#xff0c;如果在页面显示0…

SpringBoot快速入门(黑马学习笔记)

需求 需求&#xff1a;基于SpringBoot的方式开发一个Web应用&#xff0c;浏览器发起请求/hello后&#xff0c;给浏览器返回字符串"Hello World~"。 开发步骤 第一步&#xff1a;创建SpringBoot工程项目 第二步&#xff1a;定义HelloController类&#xff0c;添加方…

JVM简单理解

前言 JVM,简单来说就是Java虚拟机 注意区分这里JDK JRE JVM的区别 JDK是java的开发工具包 JRE是java的运行时环境 JVM是java虚拟机 负责解释和执行java字节码 JVM拿到发布的.class文件就可以直接转换成window或其他操作系统支持的可执行指令了 主流的JVM是HotSpot 本文主要简单…

WebStorm 2023:让您更接近理想的开发环境 mac/win版

JetBrains WebStorm 2023激活版下载是一款强大而智能的Web开发工具&#xff0c;专为提高开发人员的生产力而设计。这款编辑器提供了许多先进的代码编辑功能&#xff0c;以及一系列实用的工具和插件&#xff0c;可帮助您更快地编写、调试和测试代码。 WebStorm 2023软件获取 We…

分布式概念

分布式概念 一、分布式介绍1.1 分布式计算1.1.1 分布式计算的方法1.1.1 分布式计算与互联网的普及1.1.2 分布式计算项目1.1.3 参与计算 1.2 分布式存储系统1.2.1 P2P 数据存储系统1.2.2 云存储系统 1.3 应用 二、分布式基础概念2.1 微服务2.2 集群2.3 分布式2.4 节点2.5 远程调…

【QT+QGIS跨平台编译】之五十三:【QGIS_CORE跨平台编译】—【qgssqlstatementparser.cpp生成】

文章目录 一、Bison二、生成来源三、构建过程一、Bison GNU Bison 是一个通用的解析器生成器,它可以将注释的无上下文语法转换为使用 LALR (1) 解析表的确定性 LR 或广义 LR (GLR) 解析器。Bison 还可以生成 IELR (1) 或规范 LR (1) 解析表。一旦您熟练使用 Bison,您可以使用…

【Unity】如何在Unity 中创建带有缩放效果的滚动视图(具有吸附效果的实现与优化)?

效果预览&#xff1a; 目录 效果预览&#xff1a; 一、引言&#xff1a; 二、问题描述 三、解决方案&#xff1a; 三、优化&#xff1a; 四、结论 一、引言&#xff1a; 在Unity开发中&#xff0c;经常需要实现滚动视图&#xff08;ScrollView&#xff09;中的内容吸附到…

鸿蒙 渲染控制

前提&#xff1a;基于官网3.1/4.0文档。参考官网文档 基于Android开发体系来进行比较和思考。&#xff08;或有偏颇&#xff0c;自行斟酌&#xff09; 1.概念 ArkUI通过自定义组件的build()函数和builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了…

【踩坑】修复xrdp无法关闭Authentication Required验证窗口

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 问题如下&#xff0c;时不时出现&#xff0c;有时还怎么都关不掉&#xff0c;很烦&#xff1a; 解决方法一&#xff1a;命令行输入 dbus-send --typemethod_call --destorg.gnome.Shell /org/gnome/Shell org.gn…

1688以图搜图API接口|c#爬虫-1688官网自动以图搜图

1688item_search_img 拍立淘 背景 在1688有个功能&#xff0c;就是上传图片&#xff0c;就可以找到类似的商品。如下 网址 &#xff1a;https://www.1688.com/ 这时候&#xff0c;我们可以使用程序来代替&#xff0c;大批量的完成图片上传功能。 实现思路 1、找到图片上传…

PFA溶样罐耐酸碱小型样品罐适用元素分析实验透明特氟龙消解瓶

PFA溶样罐&#xff0c;也叫PFA管形瓶、可溶性聚四氟乙烯溶样罐、消解瓶等&#xff0c;常用于地质地矿、地球化学、土壤微生物等样品分析消解实验&#xff0c;可搭配石墨消解仪、电热板使用。广泛适用于痕量分析、环境监测、重金属检测、半导体、新材料、新能源等。 规格参考&am…

【物联网应用案例】智能农业的 9 个技术用例

一、农业中的物联网用例 一般而言&#xff0c;农业物联网传感器以及农业物联网应用有多种类型&#xff1a; 1. 气候条件监测 气象站无疑是当今智能农业领域最受欢迎的设备。这款设备集成了多种智能农业传感器&#xff0c;能够在现场对各类数据进行收集&#xff0c;然后迅速将…

浅析扩散模型与图像生成【应用篇】(四)——Palette

4. Palette: Image-to-Image Diffusion Models 该文提出一种基于扩散模型的通用图像转换&#xff08;Image-to-Image Translation&#xff09;模型——Palette&#xff0c;可用于图像着色&#xff0c;图像修复&#xff0c;图像补全和JPEG图像恢复等多种转换任务。Palette是一种…

基于JAVA的不良邮件过滤系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统用户模块2.2 收件箱模块2.3 发件箱模块2.4 垃圾箱模块2.5 回收站模块2.6 邮箱过滤设置模块 三、实体类设计3.1 系统用户3.2 邮件3.3 其他实体 四、系统展示五、核心代码5.1 查询收件箱档案5.2 查询回收站档案5.3 新…

Python爬虫——Urllib库-上

这几天都在为了蓝桥杯做准备&#xff0c;一直在刷算法题&#xff0c;确实刷算法题的过程是及其的枯燥且枯燥的。于是我还是决定给自己找点成就感出来&#xff0c;那么Python的爬虫就这样开始学习了。 注&#xff1a;文章源于观看尚硅谷爬虫视频后笔记 目录 Urllib库 基本使…

【视频图像取证篇】Amped FIVE专业法医图像和视频增强软件之模糊图像去隔行功能

【视频图像取证篇】Amped FIVE专业法医图像和视频增强软件之模糊图像去隔行功能 法医图像和视频增强软件&#xff0c;专业又强大&#xff01;&#xff01;&#xff01;超过 140 种过滤器和工具&#xff0c;用于分析、恢复和增强数字图像和视频。Amped FIVE能够稳定抖动的视频&…