1. 程式人生 > 其它 >Spring原始碼閱讀 - @Import一個繼承 ImportBeanDefinitionRegistrar 的類

Spring原始碼閱讀 - @Import一個繼承 ImportBeanDefinitionRegistrar 的類

1. ImportBeanDefinitionRegistrar


// 和 DeferredImportSelectors 類似, 也可以自己註冊 BeanDefinition
// 不過這個介面可以繼承 EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware, 或者提供引數為 Environment、... 的建構函式\
// 不過也只是經過了特殊的的初始化, 和普通 Bean 通過 getBean 經過完整生命週期還是不一樣的
public interface ImportBeanDefinitionRegistrar {

    // 註冊BD, 預設行為啥也不做
    // 不建議在這裡注入 BeanDefinitionRegistryPostProcessor, 因為處理到他的時候, 配置類的處理流程已經完畢【??待續後續程式碼】
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
            BeanNameGenerator importBeanNameGenerator) {

        registerBeanDefinitions(importingClassMetadata, registry);
    }

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }

}

2. 處理流程

org.springframework.context.annotation.ConfigurationClassParser#processImports

// Import 的類繼承了 ImportSelector 介面
if (candidate.isAssignable(ImportSelector.class)) {
    // ...
    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                this.environment, this.resourceLoader, this.registry);
    // ...
}
// 繼承了 ImportBeanDefinitionRegistrar 介面,說明這個類有想自己向容器注入 BD 的想法,比如說 MyBatis 自己收集介面, 自己注入這些介面代理類的 BD
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    // Candidate class is an ImportBeanDefinitionRegistrar ->
    // delegate to it to register additional bean definitions
    Class<?> candidateClass = candidate.loadClass();
    // 例項化, 這個呼叫和上面的呼叫是一致的, 也就是說 ImportSelector 也可以繼承一些 Aware 介面, 或提供建構函式
    ImportBeanDefinitionRegistrar registrar =
            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                    this.environment, this.resourceLoader, this.registry);
    // 也沒有立即處理, 注意這裡是放到 configClass 中的
    // 而 DeferredImportSelector 是放到了一個集合中
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}

3. 處理時機

注意上面是將這個 ImportBeanDefinitionRegistrar 繫結在了 configClass 中,從這就可看出它的執行應該是較晚的,實際比 DeferredImportSelector 還晚。
回到 ConfigurationClassPostProcessor#processConfigBeanDefinitions

do {
    // 這裡是解析配置類, DeferredImportSelector  就是在裡面就被呼叫了
    parser.parse(candidates);
    parser.validate();

    // 這裡面應該是 Parser 記錄的解析過的配置類
    Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    // 去除已經解析過的,這個實際主要是用在後面 loadBeanDefinitions 去除不必要的 BD, removeAll 是為了減少判斷
    //  alreadyParsed 是上一次迴圈已經解析過的,去除上一次解析過的就是這一次解析過的,主要是 Parser 是複用的,所以需要 removeAll
    configClasses.removeAll(alreadyParsed);

    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
        this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
    }
    // 這裡就處理了 ImportBeanDefinitionRegistrar, 遲於 DeferredImportSelector
    this.reader.loadBeanDefinitions(configClasses);
    alreadyParsed.addAll(configClasses);
    // ...
}
while (!candidates.isEmpty());