广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >OpenFeign 基本介绍和原理了解
  • 315
分享到

OpenFeign 基本介绍和原理了解

javaspringspringcloud 2023-09-20 20:09:05 315人浏览 安东尼
摘要

了解 OpenFeign OpenFeign 组件的前身是 Netflix Feign 项目。后来 Feign 项目被贡献给了开源组织,才有了今天使用的 spring cloud OpenFeign

了解 OpenFeign

OpenFeign 组件的前身是 Netflix Feign 项目。后来 Feign 项目被贡献给了开源组织,才有了今天使用的 spring cloud OpenFeign 组件。
OpenFeign 提供了一种声明式的远程调用接口,它可以大幅简化远程调用的编程体验。用一个代码片段看一下,由 OpenFeign 发起的远程服务调用的代码风格是什么样的。

String response = helloWorldService.hello("spring Cloud");

可以发现,使用 OpenFeign 组件来实现远程调用非常简单,就像使用本地方法一样,只要一行代码就能实现 WEBClient 组件好几行代码干的事情。而且这段代码不包含任何业务无关的信息,完美实现了调用逻辑和业务逻辑之间的职责分离。

OpenFeign 组件背后的工作流程

OpenFeign 使用了一种动态代理技术来封装远程服务调用的过程,在上面的例子中看到的 helloWorldService 其实是一个特殊的接口,它是由 OpenFeign 组件中的 FeignClient 注解所声明的接口。

import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.*;@FeignClient(value = "hello-world-service")public interface HelloWorldService {     @PostMapping("/sayHello")     String hello(String guestName);}

远程服务调用的信息被写在了 FeignClient 接口中。在上面的代码里,可以看到,服务的名称、接口类型、访问路径已经通过注解做了声明。OpenFeign 通过解析这些注解标签生成一个动态代理类,这个代理类会将接口调用转化为一个远程服务调用的 Request,并发送给目标服务。

OpenFeign 的动态代理

在项目初始化阶段,OpenFeign 会生成一个代理类,对所有通过FeignClient 接口发起的远程调用进行动态代理。如图:
在这里插入图片描述
上图中的步骤中,在项目启动阶段加载完成的是 1 ~ 3步 ,只有第 4 步(调用远程服务)是发生在项目的运行阶段
关键步骤描述:

  1. 在项目启动阶段,OpenFeign 框架会发起一个主动的扫包流程,从指定的目录下扫描并加载所有被 @FeignClient 注解修饰的接口。
  2. OpenFeign 会针对每一个 FeignClient 接口生成一个动态代理对象,即图中的 FeignProxyService,这个代理对象在继承关系上属于 FeignClient 注解所修饰的接口的实例。
  3. 这个动态代理对象会被添加到 Spring 上下文中,并注入到对应的服务里,也就是图中的 LocalService 服务。
  4. LocalService 会发起底层方法调用。实际上这个方法调用会被 OpenFeign 生成的代理对象接管,由代理对象发起一个远程服务调用,并将调用的结果返回给 LocalService。

OpenFeign 是如何通过动态代理技术创建代理对象的?

OpenFeign 组件加载过程的重要阶段,如图:
在这里插入图片描述
OpenFeign 动态代理类的创建过程:

  1. 项目加载:在项目的启动阶段,EnableFeignClients 注解扮演了启动开关的角色,它使用 Spring 框架的 Import 注解导入了 FeignClientsReGIStrar 类,开始了 OpenFeign 组件的加载过程。
@SpringBootApplication@EnableFeignClientspublic class HelloWorldApplication {    public static void main(String[] args) {        SpringApplication.run(HelloWorldApplication .class, args);    }}
  1. 扫包:FeignClientsRegistrar 负责 FeignClient 接口的加载,它会在指定的包路径下扫描所有的 FeignClients 类,并构造 FeignClientFactoryBean 对象来解析 FeignClient 接口。
  2. 解析 FeignClient 注解:FeignClientFactoryBean 有两个重要的功能,一个是解析 FeignClient 接口中的请求路径和降级函数的配置信息;另一个是触发动态代理的构造过程。
  3. 构建动态代理对象:ReflectiveFeign 包含了 OpenFeign 动态代理的核心逻辑,它主要负责创建出 FeignClient 接口的动态代理对象。ReflectiveFeign 在这个过程中有两个重要任务:一个是解析 FeignClient 接口上各个方法级别的注解。将其中的远程接口 URL、接口类型(GET、POST 等)、各个请求参数等封装成元数据,并为每一个方法生成一个对应的 MethodHandler 类作为方法级别的代理;另一个重要任务是将这些 MethodHandler 方法代理做进一步封装,通过 Java 标准的动态代理协议,构建一个实现了 InvocationHandler 接口的动态代理对象,并将这个动态代理对象绑定到 FeignClient 接口上。这样一来,所有发生在 FeignClient 接口上的调用,最终都会由它背后的动态代理对象来承接。
    MethodHandler 的构建过程涉及到了复杂的元数据解析,OpenFeign 组件将 FeignClient 接口上的各种注解封装成元数据,并利用这些元数据把一个方法调用“翻译”成一个远程调用的 Request 请求。
    元数据的解析是依赖于 OpenFeign 组件中的 Contract 协议解析功能。Contract 是 OpenFeign 组件中定义的顶层抽象接口,它有一系列的具体实现,其中 springMVCContract 这个类名字中就能看出来,它是专门用来解析 Spring mvc 标签的。
    SpringMvcContract 的继承结构是 :SpringMvcContract->BaseContract->Contract。
    这里拿一段 SpringMvcContract 的代码块,理解它是如何将注解解析为元数据的。这段代码的主要功能是解析 FeignClient 方法级别上定义的 Spring MVC 注解。
// 解析FeignClient接口方法级别上的RequestMapping注解protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {      // 如果方法上没有使用RequestMapping注解,则不进行解析   // 其实GetMapping、PostMapping等注解都属于RequestMapping注解   if (!RequestMapping.class.isInstance(methodAnnotation)         && !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {      return;   }   // 获取RequestMapping注解实例   RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);   // 解析Http Method定义,即注解中的GET、POST、PUT、DELETE方法类型   RequestMethod[] methods = methodMapping.method();   // 如果没有定义methods属性则默认当前方法是个GET方法   if (methods.length == 0) {      methods = new RequestMethod[] { RequestMethod.GET };   }   checkOne(method, methods, "method");   data.template().method(Request.HttpMethod.valueOf(methods[0].name()));   // 解析Path属性,即方法上写明的请求路径   checkAtMostOne(method, methodMapping.value(), "value");   if (methodMapping.value().length > 0) {      String pathValue = emptyToNull(methodMapping.value()[0]);      if (pathValue != null) {         pathValue = resolve(pathValue);         // 如果path没有以斜杠开头,则补上/         if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {            pathValue = "/" + pathValue;         }         data.template().uri(pathValue, true);         if (data.template().decodeSlash() != decodeSlash) {            data.template().decodeSlash(decodeSlash);         }      }   }   // 解析RequestMapping中定义的produces属性   parseProduces(data, method, methodMapping);   // 解析RequestMapping中定义的consumer属性   parseConsumes(data, method, methodMapping);   // 解析RequestMapping中定义的headers属性   parseHeaders(data, method, methodMapping);   data.indexToExpander(new LinkedHashMap<>());}

通过上面的方法可以看到,OpenFeign 对 RequestMappings 注解的各个属性都做了解析。
如果项目中使用的是 GetMapping、PostMapping 之类的注解,没有使用 RequestMapping,那么 OpenFeign 也可以解析。以 GetMapping 为例,它对 RequestMapping 注解做了一层封装。如下代码片段,这个注解头上也挂了一个 RequestMapping 注解。因此 OpenFeign 可以正确识别 GetMapping 并完成加载。

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@RequestMapping(method = RequestMethod.GET)public @interface GetMapping {// ...省略部分代码}

OpenFeign调用过程

OpenFeign 其实底层调用的是 Feign 的方法,生成了代理类,使用的是 jdk 的动态代理,然后 bean 注入。
调用过程,就是代理类作为客户端向被调用方发送请求,接收相应的过程。其中,feign 自行封装了 JDK java.net 相关的网络请求方法,请求过程中还有 Loadbalancer 进行负载均衡;收到响应后,还会对响应类进行解析,取出正确的响应信息。

来源地址:https://blog.csdn.net/weixin_40364776/article/details/128695858

--结束END--

本文标题: OpenFeign 基本介绍和原理了解

本文链接: https://www.lsjlt.com/news/413247.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作