首页 > 其他分享 >OpenFeign 服务注册和调用原理

OpenFeign 服务注册和调用原理

时间:2024-08-15 17:29:14浏览次数:5  
标签:调用 String OpenFeign contextId registry 注册 attributes class FeignClientsRegistrar

Feign 注册到容器

和 springboot 自动配置原理类似,在 springboot 启动时会扫描到 EnableFeignClients 注解,这个注解导入了一个 FeignClientsRegistrar 类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {}

FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar,所以会通过 registerBeanDefinitions 方法注入一批 BeanDefinition

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 注册配置类(上一篇文章说过的 EnableFeignClients.defaultConfiguration 属性)
    registerDefaultConfiguration(metadata, registry);
    // 注册 feign 客户端
    registerFeignClients(metadata, registry);
}

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients
// registerFeignClients,参数应该不陌生了,一个是元注解信息,一个是 ImportBeanDefinitionRegistrar 用来注册 bean 的
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

    LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
    // 通过元注解获取到注解属性(就是 EnableFeignClients 的配置项)
    Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
    // 先看有没有配 clients
    final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        // 扫描使用了 FeignClient 注解的类 
        scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        Set<String> basePackages = getBasePackages(metadata);
        for (String basePackage : basePackages) {
            candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        }
    }
    else {
        // clients 指定的类
        for (Class<?> clazz : clients) {
            candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
        }
    }

    for (BeanDefinition candidateComponent : candidateComponents) {
        if (candidateComponent instanceof AnnotatedBeanDefinition) {
            ...
            registerFeignClient(registry, annotationMetadata, attributes);
        }
    }
}

实际注册 bean 的方法 registerFeignClient,里面根据配置信息构建了一个 BeanDefinition,构建的过程还是比较简单就是设置 beanName、type、lazy 等基础信息和 EnableFeignClients 配置的 fallback 等

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    // 是否延迟加载,读取的配置项,true:使用 feign 时才创建;false(默认):项目启动时创建
    if (String.valueOf(false).equals(this.environment.getProperty("spring.cloud.openfeign.lazy-attributes-resolution", String.valueOf(false)))) {
        this.eagerlyRegisterFeignClientBeanDefinition(className, attributes, registry);
    } else {
        this.lazilyRegisterFeignClientBeanDefinition(className, attributes, registry);
    }
}

// org.springframework.cloud.openfeign.FeignClientsRegistrar#eagerlyRegisterFeignClientBeanDefinition
private void eagerlyRegisterFeignClientBeanDefinition(String className, Map<String, Object> attributes, BeanDefinitionRegistry registry) {
    this.validate(attributes);
    // 构建一个 BeanDefinitionBuilder(genericBeanDefinition 方法把 bealClass 设置为 FeignClientFactoryBean)
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
    definition.addPropertyValue("url", this.getUrl((ConfigurableBeanFactory)null, attributes));
    definition.addPropertyValue("path", this.getPath((ConfigurableBeanFactory)null, attributes));
    String name = this.getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = this.getContextId((ConfigurableBeanFactory)null, attributes);
    definition.addPropertyValue("contextId", contextId);
    
    // 省略了 BeanDefinitionBuilder 的赋值操作
    ...
    
    // 根据 beanDefinition 再构建一个 BeanDefinitionHolder
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
    // 关联 BeanDefinitionHolder 和 registry(registry 持有 BeanDefinitionHolder,后续通过 registry 创建对象)
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    
    // 方法调用了两次,但是参数不一样,一个是创建 Feign 的配置项(Request.Options,超时配置),一个是 Feign
    this.registerRefreshableBeanDefinition(registry, contextId, Request.Options.class, OptionsFactoryBean.class);
    this.registerRefreshableBeanDefinition(registry, contextId, RefreshableUrl.class, RefreshableUrlFactoryBean.class);
}

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerRefreshableBeanDefinition
private void registerRefreshableBeanDefinition(BeanDefinitionRegistry registry, String contextId, Class<?> beanType,
        Class<?> factoryBeanType) {
    if (isClientRefreshEnabled()) {
        String beanName = beanType.getCanonicalName() + "-" + contextId;
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(factoryBeanType);
        definitionBuilder.setScope("refresh");
        definitionBuilder.addPropertyValue("contextId", contextId);
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definitionBuilder.getBeanDefinition(),
                beanName);
        // 反射创建对象(前面说了 beanClass 是 FactoryBean)
        definitionHolder = ScopedProxyUtils.createScopedProxy(definitionHolder, registry, true);
        // 注册 beanDefinition
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }
}

FactoryBean 这个在前面的文章有说过,这是 spring 的知识,工厂方式构建 bean 的方式:1,使用静态方法;2:使用实例方法;3:使用 FactoryBean。如果是 FactoryBean,最终的对象是 getObject 方法返回的对象作为 bean,最终的代理对象怎么生成的就不贴源码了,代码量也不多,看下最终的代理对象长啥样吧

Feign 调用流程

就是把容器中 Feign 的代理对象取出来,然后反射执行方法的过程,先对比下刚才放到单例池的代理对象和注入的对象

先判断是否是 Object 的方法,然后执行代理对象的方法,最后发送一个 http 请求,调用链如下

标签:调用,String,OpenFeign,contextId,registry,注册,attributes,class,FeignClientsRegistrar
From: https://www.cnblogs.com/cyrushuang/p/18301838

相关文章

  • OpenFeign 使用细节
    @EnableFeignClients注解配置项@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Import({FeignClientsRegistrar.class})public@interfaceEnableFeignClients{//和basePackages互为别名String[]value()default{};//......
  • 线程池使用场景 调用多个微服务汇总数据
    importlombok.SneakyThrows;importjava.text.SimpleDateFormat;importjava.util.concurrent.*;publicclassT{@SneakyThrowspublicstaticvoidmain(String[]args){shopping_threadPool();}/*汇总数据使用线程池+Future耗时≈所有......
  • 易优Tag调用-Eyoucms标签手册
    【基础用法】名称:tag功能:TAG调用语法:{eyou:tagsort='now'getall='0'row='100'}<ahref='{$field.link}'>{$field.tag}</a>{/eyou:tag}参数:aid=''文档ID,在内容页可以不设置该属性typeid=''栏目ID,调取某个栏目下的全部TAG......
  • 易优Ad单条广告调用-Eyoucms标签手册
    [基础用法]名称:ad功能:获取单条广告数据语法:{eyou:adaid='广告ID'}<ahref='{$field.links}'{$field.target}><imgalt='{$field.title}'src='{$field.litpic}'/></a>{/eyou:ad}参数:aid=''指定广告IDid='&......
  • 易优User登录注册标签-Eyoucms标签手册
    user登录注册入口标签[基础用法]名称:user功能:动态显示登录、注册、退出、会员中心的入口;语法:{eyou:usertype='login'}<ahref="{$field.url}"id="{$field.id}">登录</a>{$field.hidden}{/eyou:user}参数:type=''标签类型-......
  • Windows通过dynv6提供免费的IPv6动态域名解析(DDNS)服务(注册服务的方式运行)
    Dynv6IPv6Updater项目简介特性使用方法环境依赖运行脚本参数说明示例日志输出Windows服务注册步骤1:下载并安装NSSM步骤2:准备Python环境和脚本步骤3:使用NSSM注册服务步骤4:启动服务并验证步骤5:设置日志记录(可选)步骤6:重启系统并验证附:以下为帮......
  • 点击识别按钮调用后端接口,中途按下结束识别,但是识别还是进行啦js
    在JavaScript中,如果你想要在点击按钮后调用一个接口,并且在这个过程中按下一个按钮来中断或取消这个请求,你可以使用fetchAPI来发起请求,并使用AbortController来取消这个请求。以下是一个简单的例子://获取按钮元素conststartButton=document.getElementById('startButton');......
  • 身份证OCR识别接口如何用Java调用
    一、什么是身份证OCR识别接口?身份证OCR识别接口又叫身份证识别,身份证图像识别,身份证文字识别,即自动识别和提取身份证上的文字和数字信息。它可以通过图像处理和模式识别算法,将身份证中的姓名、性别、民族、出生日期、住址、身份证号、签发机关、有效期限等关键信息准确地提取......
  • Python代码调用扣子平台大模型,结合wxauto优秀开源项目实现微信自动回复好友消息
    最近看到微信自动化回复,觉得很有意思,想接通大模型,自动回复好友消息。以下文章将对代码进行详细解释,文章末尾附源码1.在抖音扣子平台创建发布一个大模型智能问答助手,获取API-key等。在扣子平台有详细文档。2.wxauto安装。pipinstallwxauto项目地址是​​​​​​cluic/wxau......
  • zabbix-自动注册
    一、自动注册原理自动注册,主要是Agent主动向zabbixserver注册;自动注册主要分为两个步骤:自动注册,客户端必须开启主动模式,并设定主机名在zabbixweb的告警-->动作-->自动注册动作,创建一个动作二、自动注册实践根据不同的主机名称或主机元数据关联不同的模板1、配置Ag......