一、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方法來獲取。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM