广告
返回顶部
首页 > 资讯 > 精选 >Spring Cloud中Feign的实现原理是什么
  • 240
分享到

Spring Cloud中Feign的实现原理是什么

2023-06-20 12:06:01 240人浏览 薄情痞子
摘要

本篇内容主要讲解“spring cloud中Feign的实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“spring Cloud中Feign的实现原理是什么”吧!目录一、什么是Fei

本篇内容主要讲解“spring cloud中Feign的实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习spring Cloud中Feign的实现原理是什么”吧!

目录
  • 一、什么是Feign

  • 二、为什么用Feign

  • 三、实例

    • 1、原生使用方式

    • 2、结合 Spring Cloud 使用方式

  • 四、探索Feign

    • 五、总结

      一、什么是Feign

      Feign 是⼀个 Http 请求的轻量级客户端框架。通过 接口 + 注解的方式发起 HTTP 请求调用,面向接口编程,而不是像 Java 中通过封装 HTTP 请求报文的方式直接调用。服务消费方拿到服务提供方的接⼝,然后像调⽤本地接⼝⽅法⼀样去调⽤,实际发出的是远程的请求。让我们更加便捷和优雅的去调⽤基于 HTTP 的 api,被⼴泛应⽤在 Spring Cloud 的解决⽅案中。开源项目地址:Feign,官方描述如下:

      Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and websocket. Feign's first Goal was reducing the complexity of binding Denominator unifORMly to HTTP APIs regardless of ReSTfulness.

      二、为什么用Feign

      Feign 的首要目标就是减少 HTTP 调用的复杂性。在微服务调用的场景中,我们调用很多时候都是基于 HTTP 协议的服务,如果服务调用只使用提供 HTTP 调用服务的 HTTP Client 框架(e.g. Apache HttpComponnets、HttpURLConnection OkHttp 等),我们需要关注哪些问题呢?

      Spring Cloud中Feign的实现原理是什么

      相比这些 HTTP 请求框架,Feign 封装了 HTTP 请求调用的流程,而且会强制使用者去养成面向接口编程的习惯(因为 Feign 本身就是要面向接口)。

      三、实例

      3.1、原生使用方式

      以获取 Feign 的 GitHub 开源项目的 Contributors 为例,原生方式使用 Feign 步骤有如下三步(这里以使用 Gradle 进行依赖管理的项目为例):

      引入相关依赖:implementation 'io.github.openfeign:feign-core:11.0'

      在项目的 build.gradle 文件的依赖声明处 dependencies 添加该依赖声明即可。

      声明 HTTP 请求接口

      使用 Java 的接口和 Feign 的原生注解 @RequestLine 声明 HTTP 请求接口,从这里就可以看到 Feign 给使用者封装了 HTTP 的调用细节,极大的减少了 HTTP 调用的复杂性,只要定义接口即可。

      Spring Cloud中Feign的实现原理是什么

      配置初始化 Feign 客户端

      最后一步配置初始化客户端,这一步主要是设置请求地址、编码(Encoder)、解码(Decoder)等。

      Spring Cloud中Feign的实现原理是什么

      通过定义接口,使用注解的方式描述接口的信息,就可以发起接口调用。最后请求结果如下:

      Spring Cloud中Feign的实现原理是什么

      3.2、结合 Spring Cloud 使用方式

      同样还是以获取 Feign 的 GitHub 开源项目的 Contributors 为例,结合 Spring Cloud 的使用方式有如下三步:

      引入相关 starter 依赖:org.springframework.cloud:spring-cloud-starter-openfeign

      在项目的 build.gradle 文件的依赖声明处 dependencies 添加该依赖声明即可。

      在项目的启动类 XXXApplication 上添加 @EnableFeignClients 注解启用 Feign 客户端功能。

      Spring Cloud中Feign的实现原理是什么

      创建 HTTP 调用接口,并添加声明 @FeignClient 注解。

      最后一步配置初始化客户端,这一步主要是设置请求地址(url)、编码(Encoder)、解码(Decoder)等,与原生使用方式不同的是,现在我们是通过 @FeignClient 注解配置的 Feign 客户端属性,同时请求的 URL 也是使用的 Spring mvc 提供的注解。

      Spring Cloud中Feign的实现原理是什么

      测试类如下所示:

      Spring Cloud中Feign的实现原理是什么

      运行结果如下:

      Spring Cloud中Feign的实现原理是什么

      可以看到这里是通过 @Autowired 注入刚刚定义的接口的,然后就可以直接使用其来发起 HTTP 请求了,使用是不是很方便、简洁。

      四、探索Feign

      从上面第一个原生使用的例子可以看到,只是定了接口并没有具体的实现类,但是却可以在测试类中直接调用接口的方法来完成接口的调用,我们知道在 Java 里面接口是无法直接进行使用的,因此可以大胆猜测是 Feign 在背后默默生成了接口的代理实现类,也可以验证一下,只需在刚刚的测试类 debug 一下看看接口实际使用的是什么实现类:

      Spring Cloud中Feign的实现原理是什么

      从 debug 结果可知,框架生成了接口的代理实现类 HardCodedTarget 的对象 $Proxy14 来完成接口请求调用,和刚刚的猜测一致。Feign 主要是封装了 HTTP 请求调用,其整体架构如下:

      Spring Cloud中Feign的实现原理是什么

      测试类代码里面只在 GitHub github = Feign.builder().target(GitHub.class, "https://api.github.com"); 用到了 Feign 框架的功能,所以我们选择从这里来深入源码,点击进入发现是 Feign 抽象类提供的方法,同样我们知道抽象类也是无法进行初始化的,所以肯定是有子类的,如果你刚刚有仔细观察上面的 debug 代码的话,可以发现有一个 ReflectiveFeign 类,这个类就是抽象类 Feign 的子类了。抽象类 feign.Feign 的部分源码如下:

      public abstract class Feign {      ...    public static Builder builder() {    return new Builder();  }  public abstract <T> T newInstance(Target<T> target);  public static class Builder {    ...    private final List<RequestInterceptor> requestInterceptors = new ArrayList<RequestInterceptor>();    private Logger.Level logLevel = Logger.Level.NONE;    private Contract contract = new Contract.Default();    private Client client = new Client.Default(null, null);    private Retryer retryer = new Retryer.Default();    private Logger logger = new NoOpLogger();    private Encoder encoder = new Encoder.Default();    private Decoder decoder = new Decoder.Default();    private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();    private ErrorDecoder errorDecoder = new ErrorDecoder.Default();    private Options options = new Options();    private InvocationHandlerFactory invocationHandlerFactory =        new InvocationHandlerFactory.Default();    private boolean decode404;    private boolean closeAfterDecode = true;    private ExceptionPropagationPolicy propagationPolicy = NONE;    private boolean forceDecoding = false;    private List<Capability> capabilities = new ArrayList<>();    // 设置输入打印日志级别    public Builder logLevel(Logger.Level logLevel) {      this.logLevel = logLevel;      return this;    }    // 设置接口方法注解处理器(契约)     public Builder contract(Contract contract) {      this.contract = contract;      return this;    }    // 设置使用的 Client(默认使用 jdk 的 HttpURLConnection)    public Builder client(Client client) {      this.client = client;      return this;    }    // 设置重试器    public Builder retryer(Retryer retryer) {      this.retryer = retryer;      return this;    }    // 设置请求编码器     public Builder encoder(Encoder encoder) {      this.encoder = encoder;      return this;    }    // 设置响应解码器    public Builder decoder(Decoder decoder) {      this.decoder = decoder;      return this;    }    // 设置 404 返回结果解码器    public Builder decode404() {      this.decode404 = true;      return this;    }    // 设置错误解码器    public Builder errorDecoder(ErrorDecoder errorDecoder) {      this.errorDecoder = errorDecoder;      return this;    }    // 设置请求拦截器    public Builder requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) {      this.requestInterceptors.clear();      for (RequestInterceptor requestInterceptor : requestInterceptors) {        this.requestInterceptors.add(requestInterceptor);      }      return this;    }    public <T> T target(Class<T> apiType, String url) {      return target(new HardCodedTarget<T>(apiType, url));    }    public <T> T target(Target<T> target) {      return build().newInstance(target);    }  }  ...}

      可以看到在方法 publicT target(ClassapiType, String url) 中直接创建了 HardCodedTarget 对象出来,这个对象也是上面 debug 看到的对象。再继续深入,就来到了 feign.Feign 的 newInstance(Targettarget) 的方法了,是个抽象方法,其实现在子类 ReflectiveFeign 中,这个方法就是接口代理实现生成的地方,下面通过源码来看看实现逻辑是怎样的:

      public class ReflectiveFeign extends Feign {  ...    private final ParseHandlersByName targetToHandlersByName;  private final InvocationHandlerFactory factory;  private final QueryMapEncoder queryMapEncoder;  ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,      QueryMapEncoder queryMapEncoder) {    this.targetToHandlersByName = targetToHandlersByName;    this.factory = factory;    this.queryMapEncoder = queryMapEncoder;  }  @SuppressWarnings("unchecked")  @Override  public <T> T newInstance(Target<T> target) {    // <类名#方法签名, MethodHandler>,key 是通过 feign.Feign.configKey(Class targetType, Method method) 生成的    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);    // 将 Map<String, MethodHandler> 转换为  Map<Method, MethodHandler> 方便调用    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();    // 默认方法处理器    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();    for (Method method : target.type().getMethods()) {      // 跳过 Object 类定于的方法        if (method.getDeclarinGClass() == Object.class) {        continue;      } else if (Util.isDefault(method)) {        // 默认方法(接口声明的默认方法)使用默认的方法处理器          DefaultMethodHandler handler = new DefaultMethodHandler(method);        defaultMethodHandlers.add(handler);        methodToHandler.put(method, handler);      } else {        // 接口正常声明的方法(e.g. GitHub.listContributors(String, String))          methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));      }    }    // 生成 Feign 封装的 InvocationHandler    InvocationHandler handler = factory.create(target, methodToHandler);    // 基于 JDK 动态代理生成接口的代理类(e.g. Github 接口)    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),        new Class<?>[] {target.type()}, handler);    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {      defaultMethodHandler.bindTo(proxy);    }    return proxy;  }...}

      总体流程就是在方法T newInstance(Targettarget) 生成一个含有 FeignInvocationHandler 的代理对象,FeignInvocationHandler 对象会持有 Map<Method, MethodHandler> map,代理对象调用的时候进入 FeignInvocationHandler#invoke 方法,根据调用的方法来获取对应 MethodHandler,然后再 MethodHandler 完成对方法的处理(处理 HTTP 请求等)。

      下面再深入 MethodHandler,看看是如何完成对方法 HTTP 请求处理的,MethodHandler 是一个接口定义在 feign.InvocationHandlerFactory 接口中(P.S. 基础知识点,接口是可以在内部定义内部接口的哦),有两个实现类分别为 DefaultMethodHandler 和 SynchronousMethodHandler,第一个 DefaultMethodHandler 用来处理接口的默认方法,第二个是用来处理正常的接口方法的,一般情况下都是由该类来处理的。

      final class SynchronousMethodHandler implements MethodHandler {  ...  @Override  public Object invoke(Object[] argv) throws Throwable {    // 获取 RequestTemplate 将请求参数封装成请求模板      RequestTemplate template = buildTemplateFromArgs.create(argv);    Options options = findOptions(argv);    // 请求重试器    Retryer retryer = this.retryer.clone();    while (true) {      try {        // 执行请求并解码后返回          return executeAndDecode(template, options);      } catch (RetryableException e) {        try {          // 发生重试异常则进行重试处理            retryer.continueOrPropagate(e);        } catch (RetryableException th) {          Throwable cause = th.getCause();          if (propagationPolicy == UNWRAP && cause != null) {            throw cause;          } else {            throw th;          }        }        if (logLevel != Logger.Level.NONE) {          logger.logRetry(metadata.configKey(), logLevel);        }        continue;      }    }  }  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {    // 从请求模板 RequestTemplate 构造请求参数对象 Request      Request request = targetRequest(template);    if (logLevel != Logger.Level.NONE) {      logger.logRequest(metadata.configKey(), logLevel, request);    }    Response response;    long start = System.nanoTime();    try {      // 通过 client(Apache HttpComponnets、HttpURLConnection OkHttp 等)执行 HTTP 请求调用,默认是 HttpURLConnection       response = client.execute(request, options);      // ensure the request is set. TODO: remove in Feign 12      response = response.toBuilder()          .request(request)          .requestTemplate(template)          .build();    } catch (IOException e) {      if (logLevel != Logger.Level.NONE) {        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));      }      throw errorExecuting(request, e);    }    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);    if (decoder != null)      // 对返回结果进行解码操作      return decoder.decode(response, metadata.returnType());    CompletableFuture<Object> resultFuture = new CompletableFuture<>();    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,        metadata.returnType(),        elapsedTime);    try {      if (!resultFuture.isDone())        throw new IllegalStateException("Response handling not done");      return resultFuture.join();    } catch (CompletionException e) {      Throwable cause = e.getCause();      if (cause != null)        throw cause;      throw e;    }  }...}

      至此,Feign 的核心实现流程介绍完毕,从代码上看 feign.SynchronousMethodHandler 的操作相对比较简单,主要是通过 client 完成请求,对响应进行解码以及异常处理操作,整体流程如下:

      Spring Cloud中Feign的实现原理是什么

      五、总结

      Feign 通过给我们定义的目标接口(比如例子中的 GitHub)生成一个 HardCodedTarget 类型的代理对象,由 JDK 动态代理实现,生成代理的时候会根据注解来生成一个对应的 Map<Method, MethodHandler>,这个 Map 被 InvocationHandler 持有,接口方法调用的时候,进入 InvocationHandler 的 invoke 方法(为什么会进入这里?JDK 动态代理的基础知识)。

      然后根据调用的方法从 Map<Method, MethodHandler> 获取对应的 MethodHandler,然后通过 MethodHandler 根据指定的 client 来完成对应处理, MethodHandler 中的实现类 DefaultMethodHandler 处理默认方法(接口的默认方法)的请求处理的,SynchronousMethodHandler 实现类是完成其它方法的 HTTP 请求的实现,这就是 Feign 的主要核心流程。以上是 Feign 框架实现的核心流程介绍。

      到此,相信大家对“Spring Cloud中Feign的实现原理是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

      --结束END--

      本文标题: Spring Cloud中Feign的实现原理是什么

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

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

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

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

      下载Word文档
      猜你喜欢
      • Spring Cloud中Feign的实现原理是什么
        本篇内容主要讲解“Spring Cloud中Feign的实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Cloud中Feign的实现原理是什么”吧!目录一、什么是Fei...
        99+
        2023-06-20
      • Spring Cloud原理及核心组件是什么
        本篇内容介绍了“Spring Cloud原理及核心组件是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!概述毫无疑问,Sprin...
        99+
        2023-07-05
      • Spring Cloud负载均衡组件Ribbon原理是什么
        这篇文章主要介绍“Spring Cloud负载均衡组件Ribbon原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring Cloud负载均衡组件Ribbon原理是什...
        99+
        2023-06-30
      • Spring 中 load 方法的实现原理是什么?
        Spring 是一个流行的 Java 开发框架,它提供了很多方便的功能,其中之一是 load 方法。load 方法可以根据指定的类型和 ID 加载一个对象。在本文中,我们将深入探讨 Spring 中 load 方法的实现原理。 load ...
        99+
        2023-10-15
        load spring 编程算法
      • Spring中Aware接口的实现原理是什么
        今天就跟大家聊聊有关Spring中Aware接口的实现原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。使用示例:@Component&nbs...
        99+
        2022-10-19
      • 在Spring mvc中实现DispatchServlet的原理是什么
        在Spring mvc中实现DispatchServlet的原理是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。在Spring中, ContextLoaderListene...
        99+
        2023-05-31
        springmvc hs dispatchservlet
      • Spring Cloud 的核心架构原理是怎么样的
        Spring Cloud 的核心架构原理是怎么样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。最近在补一些分布式系列的面试内容,提前几个月做做准备吧,你们懂的,也跟大家分享分...
        99+
        2023-06-19
      • spring注解的底层实现原理是什么
        Spring注解的底层实现原理主要依赖于Java的反射机制。在Spring中,通过使用注解来标识类、方法或字段,从而告诉Spring...
        99+
        2023-10-09
        spring
      • spring session的原理是什么
        Spring Session是一种用于管理用户会话的框架,它通过将会话数据存储在外部存储介质中,而不是默认的内存中,来实现会话的持久...
        99+
        2023-09-21
        spring
      • spring scope的原理是什么
        Spring的Bean的作用域(scope)指定了一个Bean的实例是如何被创建和管理的。Spring框架提供了多种作用域,包括si...
        99+
        2023-08-31
        spring scope
      • Spring MVC的原理是什么
        今天小编给大家分享一下Spring MVC的原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。SpringMVC是一种...
        99+
        2023-06-27
      • Spring框架实现依赖注入的原理是什么
        这篇文章主要介绍“Spring框架实现依赖注入的原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring框架实现依赖注入的原理是什么”文章能帮助大家解决问题。Spring 框架作为 Ja...
        99+
        2023-07-06
      • spring中aop的执行原理是什么
        在Spring中,AOP(面向切面编程)的执行原理主要涉及以下几个方面:1. 切面的定义:通过注解或配置文件等方式,定义切面(Asp...
        99+
        2023-08-09
        spring aop
      • Spring Cloud中的负载均衡策略是什么
        本篇内容介绍了“Spring Cloud中的负载均衡策略是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!IRule这是所有负载均衡策略的...
        99+
        2023-06-19
      • Spring框架的原理是什么
        这篇文章主要讲解了“Spring框架的原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring框架的原理是什么”吧!简要介绍spring的原理,并结合一个简单的实例,如何配置使用...
        99+
        2023-06-03
      • Spring中Bean扫描原理是什么
        本篇内容主要讲解“Spring中Bean扫描原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring中Bean扫描原理是什么”吧!环境建设由于创建包扫描的条件很简单,只要在Xml中配...
        99+
        2023-07-02
      • Java Spring AOP源码解析中的事务实现原理是什么
        这篇文章将为大家详细讲解有关Java Spring AOP源码解析中的事务实现原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。不用Spring管理事务?让我们先来...
        99+
        2023-06-22
      • Spring Cloud中Hystrix的请求缓存怎么实现
        本篇内容介绍了“Spring Cloud中Hystrix的请求缓存怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!通过方法重载开启缓存...
        99+
        2023-06-19
      • MongoDB中实现原理是什么
        今天就跟大家聊聊有关MongoDB中实现原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。主流程MyCAT Server 接收 MySQL C...
        99+
        2022-10-18
      • Spring Boot启动的原理是什么
        本文小编为大家详细介绍“Spring Boot启动的原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Spring Boot启动的原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起...
        99+
        2022-10-19
      软考高级职称资格查询
      编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
      • 官方手机版

      • 微信公众号

      • 商务合作