1. 程式人生 > 其它 >Spring原始碼閱讀 - @Import 處理

Spring原始碼閱讀 - @Import 處理

1. 概述

同樣是 org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass 開始
org.springframework.context.annotation.ConfigurationClassParser#processImports 就是處理 @Import 的,並沒有將這個邏輯單獨抽出來,原因應該是內部處理還依賴這個類的東西。

2. @Import

注意到這個註解不是可重複註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

    /**
     * {@link Configuration @Configuration}, {@link ImportSelector},
     * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
     */
    Class<?>[] value();

}

3. 解析

org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

獲取 @Import 的類

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
    Set<SourceClass> imports = new LinkedHashSet<>();
    // 這裡的這個 visited 後面好像是沒有實際使用到的
    Set<SourceClass> visited = new LinkedHashSet<>();
    collectImports(sourceClass, imports, visited);
    return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
        throws IOException {

    if (visited.add(sourceClass)) {
        // 這裡不太能理解,為什麼會有多個 @Import 註解 ?
        for (SourceClass annotation : sourceClass.getAnnotations()) {
            String annName = annotation.getMetadata().getClassName();
            //
            if (!annName.equals(Import.class.getName())) {
                // 這裡是遞迴呼叫
                collectImports(annotation, imports, visited);
            }
        }
        imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    }
}

處理 @Import

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

    // 匯入的類為空
    if (importCandidates.isEmpty()) {
        return;
    }

    // 迴圈匯入的情況,暫時略具體邏輯
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        // push 了配置類
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                // Import 的類繼承了 ImportSelector 介面
                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    Class<?> candidateClass = candidate.loadClass();
                    // 例項化這個類
                    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                            this.environment, this.resourceLoader, this.registry);
                    Predicate<String> selectorFilter = selector.getExclusionFilter();
                    if (selectorFilter != null) {
                        exclusionFilter = exclusionFilter.or(selectorFilter);
                    }
                    // 繼承了 DeferredImportSelector 介面(ImportSelector 的子介面)
                    if (selector instanceof DeferredImportSelector) {
                        // 稍後再處理
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        // 否則立即處理,將返回的字串陣列當做類全限定名陣列,載入對應的 class 入記憶體
                        // 傳入的資訊是使用了 @Import 註解的類的資訊
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                        // 把這些載入的類當做直接 @Import 的類進行處理,注意到原 @Import 的類並未被注入容器
                        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                    }
                }
                // 繼承了 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();
                    // 例項化
                    ImportBeanDefinitionRegistrar registrar =
                            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                    this.environment, this.resourceLoader, this.registry);
                    // 也沒有立即處理, 注意這裡是放到 configClass 中的
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                    // 普通類,這裡的普通類包括 @Import 直接匯入的沒有繼承上面三個介面的類,還有就是 @Import 匯入了繼承 ImportSelector 介面的類要注入的普通類
                    // 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), exclusionFilter);
                }
            }
        }
        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 一個"普通"類和 @Import 一個繼承了 ImportSelector 介面的類的處理邏輯較為明顯,其他的實際都不是很明顯。