首页 > 编程语言 >【OpenFeign】@FeignClient 代理对象的创建源码分析

【OpenFeign】@FeignClient 代理对象的创建源码分析

时间:2024-03-23 19:34:25浏览次数:27  
标签:FeignClient OpenFeign url client 对象 源码 context 上下文

1  前言

我们从上节 【OpenFeign】@FeignClient 注入过程源码分析 继续,来看看它代理对象的创建,以及请求的执行过程。

我们就从它的 FeignClientFactoryBean 看起,那我们这里简单回忆下它都设置了哪些属性,我简单画了个图。

这些属性不了解的话,就先看看上节哈,有详细的说明,我这里就不再啰嗦了哈。

2  源码分析

2.1  入口 FeignClientFactoryBean

看到 FactoryBean 就先看它的 getObject():

// FeignClient#getObject
@Override
public Object getObject() {
    // 调用内部 getTarget()
    return getTarget();
}

继续进去看 getTarget()方法:

/**
 * @param <T> the target type of the Feign client
 * @return a {@link Feign} client created with the specified data and the context
 * information
 */
<T> T getTarget() {
    // 先拿到上下文中的 FeignContext Bean出来 这个哪来的呢?
    FeignContext context = beanFactory != null
            ? beanFactory.getBean(FeignContext.class)
            : applicationContext.getBean(FeignContext.class);
    // 拿到构建起对象 里边会设置编码器 解码器 连接器等等
    Feign.Builder builder = feign(context);
      // 如果没有设置 url 属性
    if (!StringUtils.hasText(url)) {
           // 打印下日志
        if (LOG.isInfoEnabled()) {
            LOG.info("For '" + name
                    + "' URL not provided. Will try picking an instance via load-balancing.");
        }
        // 不是 http 开头的 给你补一下
        if (!name.startsWith("http")) {
            url = "http://" + name;
        }
        else {
            url = name;
        }
        url += cleanPath();
        // 服务调用
        return (T) loadBalance(builder, context,
                new HardCodedTarget<>(type, name, url));
    }
    // 设置了 url 属性的 一样不是 http打头的 就给你补一下 看说明 openfeign 最后基本都是走的 http调用是不是
    if (StringUtils.hasText(url) && !url.startsWith("http")) {
        url = "http://" + url;
    }
    String url = this.url + cleanPath();
    // 获取一个客户端
    Client client = getOptional(context, Client.class);
    if (client != null) {
        if (client instanceof LoadBalancerFeignClient) {
            // not load balancing because we have a url,
            // but ribbon is on the classpath, so unwrap
            client = ((LoadBalancerFeignClient) client).getDelegate();
        }
        if (client instanceof FeignBlockingLoadBalancerClient) {
            // not load balancing because we have a url,
            // but Spring Cloud LoadBalancer is on the classpath, so unwrap
            client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
        }
        if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
            // not load balancing because we have a url,
            // but Spring Cloud LoadBalancer is on the classpath, so unwrap
            client = ((RetryableFeignBlockingLoadBalancerClient) client)
                    .getDelegate();
        }
        builder.client(client);
    }
    // 开始调用
    Targeter targeter = get(context, Targeter.class);
    return (T) targeter.target(this, builder, context,
            new HardCodedTarget<>(type, name, url));
}

可以看到大致分三步:

(1)从上下文获取到 FeignContext 对象,然后构建属于当前 FeignClient 的上下文对象

(2)从属于当前 FeignClient 的上下文对象中,根据当前 FeignClient 的配置创建 Feign 的构建器

(3)根据 URL 的是否设置,来创建相应的代理对象,落点都在 Targeter 对象的 target 方法来创建对象

那我们接下来看看这三步都做了什么。

2.2  FeignContext 的由来

关于 FeignContext 我之前单独写了一节【OpenFeign】【NamedContextFactory】深入剖析 NamedContextFactory 的原理以及使用 ,不知道的可以先看看它是干什么呢,有什么作用,以及怎么获取。主要就是针对每个 @FeignClient 做不同的配置,把它们的配置都隔离到了自己的上下文对象里来达到效果,我们这里就不再啰嗦了。

我们这里就主要看下它是怎么来的,以及它其中的配置的来源。首先,他能从上下文中获取,那么思考一下这个对象是什么跑到上下文的呢?就在 Feign 的自动装配类里,我们看一下:

有个配置列表,谁会提供这个列表呢?或者他的来源是哪里呢?这就需要回想到我们上节看到的 FeignClient 的 BeanDefinition 的注入过程了,我这里直接在上节的图里点缀一下,看红色的两根线:

我这里 debug 看了下,看配置列表会有一个默认的以及我的两个 @FeignClient 的配置:

好,这就是 FeignContext 的作用,主要是构建属于当前 FeignClient 的一个上下文对象。

2.3  Builder 构建器

那我们继续看看构建器对象的创建:

protected Builder feign(FeignContext context) {
    // 从上下文中获取日志
    FeignLoggerFactory loggerFactory = (FeignLoggerFactory)this.get(context, FeignLoggerFactory.class);
    // 创建日志对象
    Logger logger = loggerFactory.create(this.type);
    // 创造构建器 编码器 解码器 连接器啥的
    Builder builder = ((Builder)this.get(context, Builder.class)).logger(logger).encoder((Encoder)this.get(context, Encoder.class)).decoder((Decoder)this.get(context, Decoder.class)).contract((Contract)this.get(context, Contract.class));
    // 配置
    this.configureFeign(context, builder);
    return builder;
}

在默认什么都不配置的情况下,什么日志工厂、编码器、解码器,都是走的默认的配置,就是 FeignContext 创建该 Feign 的上下文对象中,注册了一些默认的配置,可以看 FeignClientsConfiguration。

2.4  代理对象创建

我们看到根据 URL 属性设置的有无,会走不同的逻辑,但是最后都是通过 Targeter 对象的 target 方法来创建,而 target 方法又是基于构建器对象里的 Feign 对象来创建的:

最后可以看到它是通过 JDK 动态代理的方式创建的。

3  小结

铁子们,我发现这个创建的过程,链路有点长啊,里边的配置很多,会根据不同的配置构建 feign,但是大体思路上我们基本可以看到首先是根据全局默认配置@EnableFeignClients和@FeignClient属性的配置,来构建当前 Feign 的上下文对象,然后从当前上下文中创建它的构造器,继而创建他的代理对象。后边的还没仔细看,不敢断言,所以就草草带过了= =,后续慢慢 debug 看完,再详细阐明,有理解不对的地方欢迎指正哈。

标签:FeignClient,OpenFeign,url,client,对象,源码,context,上下文
From: https://www.cnblogs.com/kukuxjx/p/18089718

相关文章

  • 在线客服系统php网站源码 支持消息预知
    新增消息预知,消息撤回,消息已读未读,修复需要刷新才能收到消息修复客户来源地址修复消息提示音修复桌面推送提醒下载地址:消息预知在线客服系统php网站源码-麦田吧要求服务器环境:宝塔面板,Nginx1.16-1.18,7.2.23<php<7.3,Mysql5.6-Mysql5.7站点设置点击伪静态选择thin......
  • 五、Spring源码学习之postProcessBeanFactory方法
    简介在应用程序上下文完成其标准初始化后,修改其内部的BeanFactory。此时,所有的bean定义都已经加载完成,但还没有任何bean被实例化。这允许在某些ApplicationContext实现中注册特殊的BeanPostProcessor等。在应用程序上下文的初始化过程中,会经历多个阶段。其中,一个关键阶段......
  • Java毕业设计-基于SSM框架的在线课堂系统项目实战(附源码+论文)
    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 毕业设计:基于SSM的电影购票系统(源码+论文)
    本项目以SSM框架为开发技术,实现了一个电影购票系统。电影购票系统的主要使用者分为管理员;首页、个人中心、用户管理、电影类型管理、放映厅管理、正在上映管理、即将上映管理、系统管理、订单管理,用户前台;首页、正在上映、即将上映、电影资讯、个人中心、后台管理、客服等功能......
  • 基于物联网的智能家居监测与控制系统(附源码+全套资料)
        本设计是基于物联网的OneNET云平台和TCP/IP网络通信设计的具有低成本高效率的智能家居控制系统。该系统通过手机APP与终端服务器设备相结合的方式进行搭建。手机APP采用C++语言通过QT开发,终端服务器设备采用ARM-Cortex_A9开发板作为服务器,M0开发板模拟温湿度数据的......
  • MyBatis3源码深度解析(二十)动态SQL实现原理(一)动态SQL的核心组件
    文章目录前言第八章动态SQL实现原理8.1动态SQL的使用8.1.1\<if>8.1.2<where|trim>8.1.3<choose|when|otherwise>8.1.4\<foreach>8.1.5\<set>8.2SqlSource组件&BoundSql组件8.3LanguageDriver组件8.3.1XMLLanguageDriver8.3.2RawLanguageDriver8.4Sq......
  • 城管智慧执法系统源码有演示,自主研发,功能完善,正版授权,可商用上项目。
     城管智慧执法系统源码有演示,自主研发,功能完善,正版授权,可商用上项目。   一套数字化的城管综合执法办案系统源码,提供了案件在线办理、当事人信用管理、文书电子送达、沿街店铺视频智能分析等功能,全面赋能执法队员,提高执法队员办案效率。手机端实现考勤、数据采集、上......
  • 【附源码】java数字家谱管理系统(ssm毕业设计+maven+vue+计算机专业)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义标题:数字家谱管理系统的选题背景及其意义随着信息技术的快速发展,数字化已经成为现代社会的一种趋势。在传统文化的传承与保护方面,数字技术的应用尤为重要。家谱作......
  • 【附源码】java双端的在线学习考试平台(ssm毕业设计+maven+vue+计算机专业)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义选题背景:在信息技术日益发展的今天,教育行业也在经历着前所未有的变革。传统的面对面教学模式逐渐向线上教育模式转变,这一趋势在全球范围内愈发明显。尤其是在全球......
  • Golang: Redislock源码分析
    Golang:Redislock源码分析源码https://github.com/bsm/redislock实现Lua脚本obtain.lua--obtain.lua:arguments=>[value,tokenLen,ttl]--Obtain.luatrytosetprovidedkeys'swithvalueandttliftheydonotexists.--Keyscanbeoverrideniftheyal......