Feign核心源码解析

Feign源码详解 揭开Feign创建以及调用的神秘面纱 阅读详情

Feign作为一个声明式的Http服务客户端,通过接口加注解的方式,就能够完成对服务提供方接口的调用,极大的简化了我们在调用服务时的工作。

那么在只有接口的条件下,Feign是如何基于接口实现服务调用的呢?在之前的代理模式及mybatis实现原理的文章中,我们知道了可以通过动态代理的方式生成代理对象。Feign是否这样实现的呢,我们从源码角度进行分析。

1、初始化阶段

首先看一下Feign的开启注解@EnableFeignClients:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
  String[] value() default {};
  String[] basePackages() default {};
  Class<?>[] basePackageClasses() default {};
  Class<?>[] defaultConfiguration() default {};
  Class<?>[] clients() default {};
}

@Import导入了FeignClientsRegistrar,该类实现了ImportBeanDefinitionRegistrar接口,在该接口的registerBeanDefinitions方法中,spring向外暴露了BeanDefinitionRegistry注册器。用户如果需要手动创建或修改BeanDefinition,可以通过把BeanDefinition注册到BeanDefinitionRegistry的方式,之后spring会帮我们实例化bean并放在容器中。

里面两个方法中,registerDefaultConfiguration方法主要用于读取配置信息,我们主要看一下registerFeignClients方法的实现:

这里首先定义了一个扫描器,并读取@EnableFeignClients注解的属性,配置FeignClient的注解类型过滤器,用以在后面进行进行包扫描操作。

通过扫描,得到所有在basepackage定义的路径下的被@FeignClient注解标记的类的BeanDefinition。

读取@FeignClient注解的内容,并存放在一个Map中,由于我在注解中只指定了name,因此只存在name和value的值(value通过@AliasFor指定为name的别名)

之后,调用registerFeignClient方法:

注意这里通过BeanDefinitionBuilder创建的是一个FeignClientFactoryBean类型的工厂bean,注意通过它的getObject返回的才是我们的FeignClient。之后通过BeanDefinitionBuilder填充FeignClient对象的属性,并获得BeanDefinition。

这里的BeanDefinitionHolder可以理解为BeanDefinition的包装类,提供了根据beanName获取BeanDefinition的方法,可以理解为额外加了一层封装。

完成属性填充后,通过Spring提供的registerBeanDefinition方法向BeanDefinitionRegistry注册了刚实例化的这个BeanDefinitionHolder。这里完成的是将FeignClient注解的类的信息交给工厂bean代理类,并将代理类的定义注册到Spring的容器中。

至此,已经把要创建的接口代理对象的信息放入registry里面,之后spring在启动调用refresh方法的时候会负责bean的实例化。在实例化过程中,调用FeignClientFactoryBean的getObject方法:

调用loadBalance方法:

这里创建的Client实例是一个LoadBalancerFeignClient的对象。Client是一个非常重要的组件,看一下配置类中注入的实例:

@Configuration
class DefaultFeignLoadBalancedConfiguration {
  @Bean
  @ConditionalOnMissingBean
  public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                SpringClientFactory clientFactory) {
    return new LoadBalancerFeignClient(new Client.Default(null, null),
        cachingFactory, clientFactory);
  }
}

在没有配置Client的情况下,会注入一个LoadBalancerFeignClient,其中delegate属性中注入了一个Client$Default对象,我们可以暂时理解为代理,后面就会讲到,Feign发送Request请求以及接收Response响应,都是借助Client$Default对象完成的。

可以回想一下之前Ribbon中讲过的RibbonLoadBalancerClient,Ribbon是使用拦截器后调用了它的execute方法。那么我们可以猜测一下,这里是不是使用什么方式最终了调用LoadBalancerFeignClient的execute方法呢?这个问题我们放在后面去证实。

2、创建代理对象

接着看上面loadBalance方法中,首先调用了HystrixTargeter的target方法:

之后调用了Feign的target方法:

最终调用了ReflectiveFeign类中的newInstance方法。其中名为nameToHandler的Map中存储了FeignClient接口中定义的方法:

看到下面的InvocationHandler和Proxy就很清楚了,和我们在开头说的一样,这里是使用JDK动态代理的方式创建代理对象。创建InvocationHandler及代理对象过程:

这里的factory是InvocationHandlerFactory的对象,看一下它的create方法,用于创建FeignInvocationHandler实例来对方法进行拦截。在构造方法中传入了代理类的接口,以及需要代理的方法:

3、拦截方法

通过JDK动态代理我们知道,在InvocationHandler中,invoke方法对进行方法拦截和逻辑增强。那么我们使用一个测试接口,看一下关键的invoke方法是如何工作的:

首先根据方法名去判断是不是Object类内置的一些方法,都不是则往下,执行了一个分发的操作,这个dispatch是初始化阶段生成的MethodHandler列表。调用SynchronousMethodHandler类的invoke方法:

使用RequestTemplate创建了一个http请求的模板,可以看见这里创建了一个请求:

GET /user/1 HTTP/1.1

进入executeAndDecode方法,在该方法中,首先使用刚才创建的模板生成了一个Request请求,并且把我们本次调用的服务名和接口名拼接在了一起:

这里把请求交给了之前创建的LoadBalancerFeignClient,执行了它的execute方法。和开头说的一样,和Ribbon类似的调用流程。只不过需要区别一下的是,Ribbon是使用拦截器拦截请求,而Feign是使用动态代理的invoke方法对方法进行拦截并转发。

进入LoadBalancerFeignClient的execute方法,在其中构建了一个RibbonRequest的请求:

在上面的uriWithoutHost中,去除了url中的服务名。这么做是因为Feign其实只需要这个服务后面的接口字符串,至于如何选择服务与负载均衡,都交给了Ribbon去做。

进入RibbonRequest的构造方法中,可以看见,用的Client的实现类是Client$Default对象,即前面讲到的在配置文件中,LoadBalancerFeignClient中delegate存储的对象。

调用AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法:

进入其submit方法:

进入selectServer方法:

进入LoadBalancerContext的getServerFromLoadBalancer方法:

在这里结合了Ribbon,完成负载均衡,根据负载均衡算法选择Server。

之后通过调用FeignLoadBalancer的execute方法,再调用Client$Default的execute方法:

调用convertAndSend创建了一个HttpURLConnection的连接,最后发起远程调用还是用的HttpURLConnection,并在convertResponse方法中封装结果,至此一次调用过程完成。

到这里,我们就能明白为什么说Feign是一个Web客户端并不准确,其实它并没有完成任何请求处理操作,只是一个伪客户端,最终还是调用其他组件完成的请求发送与接收。

最后,对Feign的实现流程进行一下总结:

1、使用JDK动态代理为接口创建代理对象

2、执行接口的方法时,调用代理对象的invoker方法

3、读取FeignClient的注解得到要调用的远程服务的接口

4、通过Ribbon负载均衡得到一个要调用的服务提供者

5、使用HttpURLConnection发起请求,得到响应

深入分析Feign源码 我们在想为什么我们调用接口StockFeignClient就能调用对应的服务呢?StockFeignClient接⼝打个@FeignClient注解,它是怎么通过接⼝上的信息、找到接⼝的实现类的呢? 阅读详情

相关推荐

Spring 源码硬核解析系列专题(二十一):Spring Cloud OpenFeign 的声明式服务调用源码解析

在前几期中,我们从 Spring 核心到 Spring Boot 的多个模块,再到 Spring Cloud Gateway,逐步揭示了 Spring 生态在微服务领域的广泛应用。Spring Cloud OpenFeign 是 Spring Cloud 提供的声明式 HTTP 客户端,简化了服务调用。本篇将深入 Spring Cloud OpenFeign 的源码,剖析其核心机制与实现原理,并补充图示。

资深架构师分享:深入剖析软件架构,探讨云计算、微服务、大数据等前沿技术。分享实践经验,促进技术交流,共同构建卓越系统。 904

Feign源码解析

代码示例 启动类上添加@EnableFeignClients注解,表示开启feign功能

指尖凉的博客 2707

17.springcloud_openfeign之扩展组件一

前面介绍了springcloud_openfeign可以从父子容器中获取对应的组件从而构建对象, 现在的企业级开发中, 一般我们都使用的springboot, 而springboot是约定大于配置的, 那么它提供的一套默认配置是什么呢, 本节我们就来认识一下。约定大于配置(Convention Over Configuration) 是 Spring Boot 的核心设计理念之一,它的主要目的是减少开发者的配置负担,让开发者专注于业务逻辑的实现,而非繁杂的配置。基本含义约定大于配置(Convention O

joeyhhhhh的博客 954

七、Feign源码分析

一、准备 @EnableFeignClients注解开启feign @SpringBootApplication @EnableFeignClients //@RibbonClients 注意 配置类不能写在@SpringbootApplication注解的@CompentScan扫描得到的地方 /*@RibbonClients( value = { @RibbonClient(name = "order-server",configuration = RuleConfig.class) }

u010342147的博客 1424

我的架构梦:(三十八)SpringCloud核心组件之Feign核心源码剖析

后续分析。。。

riemann_的博客 927

深入解析Spring Cloud微服务框架源码

Spring Cloud是一套微服务架构下的工具集,旨在简化分布式系统的开发,如配置管理、服务发现、断路器、智能路由、微服务网关等。其背后采用Spring Boot为运行框架,通过特定的注解、配置文件和依赖管理实现快速开发微服务。Spring Cloud的出现,促进了微服务架构的流行,特别是对于Java开发者,它提供了一套开箱即用的解决方案,极大地降低了微服务的开发难度。在本章中,我们将探讨Spring Cloud的核心组件及其如何协同工作以构建复杂的微服务系统。

weixin_42511373的博客 824

springcloud-feign核心源码解析一

一.@EnableFeignClients springboot集成的自动配置阅读源码首先搜 @EnableFeignClients, 发现这个enable注解导入了FeignClientsRegistrar类。 FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,如果读过spring源码的话知道spring会调用registerBeanDefinitions方法来注入bean。如下图 看这两行包装过的代码就知道这里注入的是默认的配置类和fei

那年的夏天的博客 247

【学习笔记】人群归因分数 PAF 以及combined PAF(更新)

在此推荐2篇发表在lancet以及jama子刊上的paf文章,这两篇文章套路是一样的,只是在不同国家进行。希望对大家学习PAF有所帮助,后面有新内容也会继续分享,也欢迎大家分享使用R语言计算PAF的经验~~~的时候,先建立了相关矩阵,再做主成分分析,得到communality。详细信息大家可翻阅原文。

weixin_53499537的博客 1498

SpringBoot Feign源码级解析(16)

Feign是一个声明式的HTTP客户端,由Netflix开发并被Spring Cloud集成,旨在简化微服务之间的HTTP调用。通过定义接口并添加注解,Feign能够自动创建实现类并处理HTTP请求,使服务间调用更加便捷。

Android开发领域创作者 1738

影刀RPA验证码识别避坑指南:官方指令、Python ddddocr与第三方图鉴怎么选?

本文深度对比了影刀RPA验证码识别的三种方案:官方AI指令、Python ddddocr开源库和第三方图鉴服务。从识别率、成本、维护性等维度分析各方案优劣,提供实战选型建议和优化技巧,帮助技术决策者在自动化流程中高效解决验证码识别难题。

weixin_30790841的博客 471

【python快速编程入门(第二版)黑马程序员课后编程题】

python快速编程入门(第二版)黑马程序员课后编程题

weixin_46167259的博客 1万+

erlang rabbitmq源码解析_SpringCloud服务调用源码解析汇总

相信我,你会收藏这篇文章的,本篇文章涉及Ribbon、Hystrix、Feign三个组件的源码解析Ribbon架构剖析这篇文章介绍了Ribbon的基础架构,也就是下图涉及到的6大组件:Ribbon源码解析这篇文章以上篇文章为基础,详细介绍了Ribbon的代码执行流程:Ribbon的自动装配->请求拦截器->负载均衡Hystrix工作流程解析这篇文章详细介绍了Hystrix的工...

weixin_29331689的博客 132

逆向工程实战:解密AppleAccount的X-MMe-Nas-Qualify签名生成机制

本文通过逆向工程实战,详细解析了AppleAccount服务中X-MMe-Nas-Qualify签名的生成机制。作者利用Frida进行动态追踪定位签名源头,结合IDA Pro进行静态分析,并借助LLDB动态调试还原核心算法。文章最终分享了如何独立于苹果官方库,通过Python代码复现签名算法,为自动化测试和深度交互苹果服务的开发者提供了关键技术路径。

weixin_29323049的博客 707

Feign源码分析(without spring)

Feign

dark 686

机器学习笔记之玻尔兹曼机(一)基本介绍

在受限玻尔兹曼机——模型表示(Representation)一节中以玻尔兹曼机为引,介绍了受限玻尔兹曼机。从本节开始,将正式介绍玻尔兹曼机。相比于它的表示和推断过程,我们更关注它在求解过程中出现的问题。

静静的学习就好 7924

别再手动填序号了!用Python脚本在Arcgis属性表里实现分组自动编号(附完整代码)

本文详细介绍了如何利用Python脚本在Arcgis属性表中实现分组自动编号,告别低效的手工操作。通过核心代码解析和实战教程,帮助GIS工程师快速掌握自动化编号技术,提升工作效率并降低错误率。特别适合城市规划、国土调查等需要处理大量分组数据的场景。

weixin_42532473的博客 309

Java面试:SpringBoot与SpringCloud源码深度解析

本次面试通过层层深入的提问,全面考察了候选人对SpringBoot和SpringCloud源码的理解和实战能力。候选人的回答不仅展现了扎实的理论基础,还体现了丰富的项目经验。面试官对候选人的表现给予了高度评价,认为其是团队中不可多得的技术人才。

南客先生的博客 870

FPGA/IC 秋招笔试/面试题总结(续)

FPGA/IC 秋招笔试/面试题总结(续)来啦… 文章目录二十七、状态机1. Moore/Mealy状态机2.一、二、三段式状态机 二十七、状态机 1. Moore/Mealy状态机 (1)Moore状态机: 输出只由当前状态决定,即次态=f(现态。输入),输出=f(现态) Moore型状态机的输出信号是直接由状态寄存器译码得到的。 Moore型状态机检测序列1101如下 Moore型状态机输出函数只由状态变量决定,想要输出Z=1,必须S4状态形成,即寄存器中的1101都打进去后才可以。输出Z=1会在

Crazzy_M的博客 2万+

Feign 调用机制与动态代理实现原理

本文深入解析Feign微服务调用核心机制,重点剖析动态代理实现原理和请求处理流程。从FeignClientFactoryBean源码分析入手,揭示代理对象创建过程,详细讲解请求模板构建、编码解码、拦截器链等关键环节。通过组件关系图和核心接口定义,系统阐述Feign与Ribbon的协作机制。文章还包含性能优化建议和生产环境实践经验,涵盖日志配置、异常处理等实用技巧。适合需要深度理解Feign工作原理及优化微服务调用的开发者阅读。

qq_43414012的博客 1297

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Spring源码学习

1.BeanPostProcessor:对bean的加强 2.BeanFactoryPostProcessor:对beanFacory的加强 3.FactoryBean:是一个bean,能够生产和装饰一个bean 4.BeanFactory:(root interface)一个factory,IOC容器的对象工厂 5.Enviroment 6.Event事件机制 7.xxxAware接口注入spr...

BorisCao的博客 169

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Feign重试机制深入剖析:原理、配置与实战避坑指南

通过自定义Retryer@Bean// 最大重试3次,初始间隔100ms,最大间隔1s2.2 结合Ribbon的重试配置(Feign + Ribbon) 若项目中整合了Ribbon,需注意Feign与Ribbon重试的叠加问题。# 关闭Feign的重试(避免双重重试)feign:client:config:default:# 启用Ribbon重试ribbon:MaxAutoRetries: 2 # 同一实例重试次数。

wx19930913的博客 777

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

深入Feign.builder():从源码到实战,彻底搞懂SpringCloud OpenFeign的“手动挡”模式

本文深入解析SpringCloud OpenFeign的Feign.builder()手动模式,从源码到实战全面讲解如何自定义客户端。通过核心组件拆解、高级定制技巧和性能调优指南,帮助开发者实现动态路由、熔断降级等复杂场景,提升微服务调用的灵活性和性能。

weixin_33728268的博客 213

快速上手Spring Cloud二:核心组件解析

通过对Spring Cloud核心组件的深入解析和源码分析,我们可以更好地理解这些组件的工作原理和最佳实践。在实际应用中,我们可以根据项目的需求选择合适的组件进行集成,构建出健壮的微服务生态。

沛哥儿的专栏 1427
上一篇: Ribbon核心源码解析
下一篇: 基于Netty,20分钟手撸一个RPC框架
码农参上
博客等级 码龄17年 9050粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值