SpringBoot2 | BeanDefinition 註冊核心類 ImportBeanDefinitionRegistrar 源碼分析 (十)

微信公衆號:吉姆餐廳ak
學習更多源碼知識,歡迎關注。
在這裏插入圖片描述


SpringBoot2 | SpringBoot啓動流程源碼分析(一)

SpringBoot2 | SpringBoot啓動流程源碼分析(二)

SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)

SpringBoot2 | SpringBoot Environment源碼分析(四)

SpringBoot2 | SpringBoot自定義AutoConfiguration | SpringBoot自定義starter(五)

SpringBoot2 | SpringBoot監聽器源碼分析 | 自定義ApplicationListener(六)

SpringBoot2 | 條件註解@ConditionalOnBean原理源碼深度解析(七)

SpringBoot2 | Spring AOP 原理源碼深度剖析(八)

SpringBoot2 | SpingBoot FilterRegistrationBean 註冊組件 | FilterChain 責任鏈源碼分析(九)

SpringBoot2 | BeanDefinition 註冊核心類 ImportBeanDefinitionRegistrar (十)

SpringBoot2 | Spring 核心擴展接口 | 核心擴展方法總結(十一)


概述

本篇來介紹一個Spring強大的擴展接口:ImportBeanDefinitionRegistrar,該接口主要用來註冊beanDefinition。很多三方框架集成Spring 的時候,都會通過該接口,實現掃描指定的類,然後註冊到spring 容器中。
比如 Mybatis 中的Mapper接口,springCloud中的 FeignClient 接口,都是通過該接口實現的自定義註冊邏輯。

Mybatis中的掃描實現類如下:

源碼分析

大致分兩個步驟來介紹:

  • 註冊所有的ImportBeanDefinitionRegistrar實現類
  • 執行所有的ImportBeanDefinitionRegistrar的邏輯
  • 自定義掃描註冊BeanDefinition組件

分別來看。


註冊所有的 ImportBeanDefinitionRegistrar 實現類

在 spring 容器啓動加載配置類階段,會執行配置類註解@Import的邏輯:
對應邏輯在ConfigurationClassParser中:

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
            throws IOException {

        if (visited.add(sourceClass)) {
            for (SourceClass annotation : sourceClass.getAnnotations()) {
                String annName = annotation.getMetadata().getClassName();
                if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
                    collectImports(annotation, imports, visited);
                }
            }
            //收集所有的導入配置類
            imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
        }
    }

上述方法利用了遞歸解析,直至獲取所有的導入類。

來看解析導入類邏輯:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

        if (importCandidates.isEmpty()) {
            return;
        }

        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
            this.importStack.push(configClass);
            try {
                for (SourceClass candidate : importCandidates) {
                //類型判斷是否爲 ImportSelector類型
                    if (candidate.isAssignable(ImportSelector.class)) {
                        // Candidate class is an ImportSelector -> delegate to it to determine imports
                        Class<?> candidateClass = candidate.loadClass();
                        ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                selector, this.environment, this.resourceLoader, this.registry);
                        if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                            this.deferredImportSelectors.add(
                                    new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                        }
                        else {
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                            processImports(configClass, currentSourceClass, importSourceClasses, false);
                        }
                    }
                    //這裏進行類型判斷是否爲 ImportBeanDefinitionRegistrar 類型
                    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        // Candidate class is an ImportBeanDefinitionRegistrar ->
                        // delegate to it to register additional bean definitions
                        Class<?> candidateClass = candidate.loadClass();
                        //直接實例化
                        ImportBeanDefinitionRegistrar registrar =
                                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                registrar, this.environment, this.resourceLoader, this.registry);
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    }
                    else {
                        // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                        // process it as an @Configuration class
                        this.importStack.registerImport(
                                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                        //遞歸解析
                        processConfigurationClass(candidate.asConfigClass(configClass));
                    }
                }
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                        configClass.getMetadata().getClassName() + "]", ex);
            }
            finally {
                this.importStack.pop();
            }
        }
    }

上述邏輯對@Import導入類進行判斷,如果爲ImportBeanDefinitionRegistrar類型,則直接實例化,並加入到ConfigurationClass集合中:

private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
            new LinkedHashMap<>();

該集合在配置類解析完之後,會單獨處理。


執行所有的 ImportBeanDefinitionRegistrar 的邏輯

ConfigurationClassBeanDefinitionReader通過loadBeanDefinitions方法來獲取所有的BeanDefinition,最終會執行以下方法:

上述方法解析並讀取配置類中的BeanDefinition,有一行比較關鍵:

loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

可以看到,該方法傳入的參數正是前面存入ConfigurationClass中的集合對象,也就是ImportBeanDefinitionRegistrar的所有實現類。
繼續來看:

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
        registrars.forEach((registrar, metadata) ->
                registrar.registerBeanDefinitions(metadata, this.registry));
    }

遍歷所有的實現類,執行註冊邏輯。
Mybatis,Feignclient大致做法:通過實現了該接口,然後註冊各自指定的類或接口:mapper接口或者feignClient接口,然後將接口聲明爲
FactoryBean,設置攔截方法,生成代理類。


自定義掃描註冊BeanDefinition組件

自定義註解:

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

自定義註冊實現類:

public class MyAutoBeanDefinitionRegistrar3 implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {


    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        ClassPathScanningCandidateComponentProvider scan = getScanner();

        //指定註解,類似於Feign註解
        scan.addIncludeFilter(new AnnotationTypeFilter(MyComponent.class));
        Set<BeanDefinition> candidateComponents = scan.findCandidateComponents("com.beanDefinition.registrar.component");

        BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

        candidateComponents.stream().forEach(beanDefinition -> {

            String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
            if (!registry.containsBeanDefinition(beanDefinition.getBeanClassName())) {
                registry.registerBeanDefinition(beanName, beanDefinition);
            }
        });
    }


    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false) {

            // FeignClient 重寫了 ClassPathScanningCandidateComponentProvider 匹配邏輯
            @Override
            protected boolean isCandidateComponent(
                    AnnotatedBeanDefinition beanDefinition) {
                if (beanDefinition.getMetadata().isIndependent()) {
                    // TODO until SPR-11711 will be resolved
                    // 判斷接口是否繼承了 Annotation註解
                    if (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata()
                            .getInterfaceNames().length == 1 && Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {
                        try {
                            Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(),
                                    MyAutoBeanDefinitionRegistrar3.this.classLoader);
                            return !target.isAnnotation();
                        } catch (Exception ex) {
                            this.logger.error(
                                    "Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex);
                        }
                    }
                    return true;
                }
                return false;

            }
        };
    }
}

具體代碼 Github:
https://github.com/admin801122/springboot2-spring5-studying/tree/master/filterChain

在這裏插入圖片描述


總結

本篇主要講述了 Spring BeanDefinition 註冊接口ImportBeanDefinitionRegistrar的用法,也是一些第三方框架整合 Spring 時的常用擴展接口。

看完本文,你應該能很好的理解@EnableFeignClients的註冊原理了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章