zoukankan      html  css  js  c++  java
  • 一、openfeign的自动配置

    所有文章

    https://www.cnblogs.com/lay2017/p/11908715.html

    正文

    openfeign是一种声明式的webservice客户端调用框架。你只需要声明接口和一些简单的注解,就能像使用普通的Bean一样调用远程服务。本文将了解一下openfeign自动配置相关的东西,看看都做了哪些东西。

    @EnableFeignClients开启openfeign

    首先,我们从@EnableFeignClients这个开关注解开始了解。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(FeignClientsRegistrar.class)
    public @interface EnableFeignClients {
    
        // ...省略配置
    }

    这个注解导入了一个类FeignClientsRegistrar,这个类实现了ImportBeanDefinitionRegistrar接口,该接口用于向Bean容器种注册添加BeanDefinition。

    为此,我们跟进FeignClientsRegistrar的registerBeanDefinitions方法,看看它注册了哪些东西。

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 注册默认配置
        registerDefaultConfiguration(metadata, registry);
        // 注册FeignClient接口的Bean
        registerFeignClients(metadata, registry);
    }

    1、registerDefaultConfiguration方法将会读取@EnableFeignClients接口的属性,如果存在自定义配置类那么就会被注册到容器中。

    2、registerFeignClients则会扫描所有注解了@FeignClient的接口,然后像spring本地Bean一样地注册到容器中。

    扫描@FeignClient注解的接口

    我们重点关注registerFeignClients方法,跟进代码

    public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
    
        Set<String> basePackages;
    
        // 获取扫描路径
    
        // 扫描所有路径,默认情况下扫描启动类下的路径
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            // 遍历扫描到的FeignClient的Bean
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
    
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
    
                    String name = getClientName(attributes);
                    // 注册FeignClient的配置
                    registerClientConfiguration(registry, name, attributes.get("configuration"));
                    // 注册FeignClient
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }

    方法中的核心逻辑就是扫描类路径,获取BeanDefinition,然后遍历进行注册。

    跟进registerFeignClient注册方法

    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        // 生成FeignClientFactoryBean这个BeanDefinition构造器
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
    
        // 将@FeignClient的注解属性添加到builder
        definition.addPropertyValue("url", getUrl(attributes));
        definition.addPropertyValue("path", getPath(attributes));
        String name = getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = getContextId(attributes);
        definition.addPropertyValue("contextId", contextId);
        definition.addPropertyValue("type", className);
        definition.addPropertyValue("decode404", attributes.get("decode404"));
        definition.addPropertyValue("fallback", attributes.get("fallback"));
        definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    
        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    
        // ...
    
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
        // 注册
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    这里值得注意的是genericBeanDefinition方法最终生成的其实是FeignClientFactoryBean,registerBeanDefinition方法注册进容器的也是FeignClientFactoryBean。

    而FeignClientFactoryBean向上直接实现了FactoryBean接口

    FactoryBean接口是spring开放出来的,用于自定义Bean的生成过程。也就是说,spring将会通过调用FeignClientFactoryBean的getObject来获取@FeignClient注解的接口对应的Bean对象。

    总结

    openfeign的自动配置过程逻辑相对比较简单,就是扫描了一下@FeignClient注解的接口,然后生成FeignClientFactoryBean的BeanDefinition给注册到容器当中。而具体的Bean对象,将会通过调用FeignClientFactoryBean的getObject方法来获取。

  • 相关阅读:
    DevExpress 最最最基础踩坑(如何设置控件属性)
    Oracle如何创建数据库(使用图形化界面,顺便提一下UTF-8和ZB16GB字符集的问题)
    ElementUI el-dialog中嵌套子页面
    Oracle详细教程(触发器,事务,存储过程,锁,游标)
    和人事交谈下来的几点感悟
    Oracle Groupby分组缺少表达式解决方法
    leetcode973. 最接近原点的 K 个点(谈谈C#中的Icomparer,ComParer,IComparable)
    APP的闪退和无响应
    APP测试的主要内容
    python数据结构
  • 原文地址:https://www.cnblogs.com/lay2017/p/11946182.html
Copyright © 2011-2022 走看看