• Spring源码阅读 @Import 一个继承 DeferredImportSelector接口的类


    1. DeferredImportSelector 接口

    具体注释待续。
    类头注释的英文翻译:ImportSelector的一个变体,在处理完所有@Configuration bean之后运行。当所选导入为@Conditional时,这种类型的选择器特别有用。
    实现也可以扩展org.springframework.core.Ordered接口,或者使用org.springframework.core.annotation.Order注解来指示相对于其他DeferredImportSelectors的优先级。
    实现还可以提供一个导入组,该导入组可以跨不同选择器提供额外的排序和过滤逻辑。
    作用

    1. 要导入的类被延迟导入
    2. 导入组?
    public interface DeferredImportSelector extends ImportSelector {
    
        /**
         * Return a specific import group.
         * <p>The default implementations return {@code null} for no grouping required.
         * @return the import group class, or {@code null} if none
         * @since 5.0
         */
        @Nullable
        default Class<? extends Group> getImportGroup() {
            return null;
        }
    
    
        /**
         * Interface used to group results from different import selectors.
         * @since 5.0
         */
        interface Group {
    
            /**
             * Process the {@link AnnotationMetadata} of the importing @{@link Configuration}
             * class using the specified {@link DeferredImportSelector}.
             */
            void process(AnnotationMetadata metadata, DeferredImportSelector selector);
    
            /**
             * Return the {@link Entry entries} of which class(es) should be imported
             * for this group.
             */
            Iterable<Entry> selectImports();
    
    
            /**
             * An entry that holds the {@link AnnotationMetadata} of the importing
             * {@link Configuration} class and the class name to import.
             */
            class Entry {
    
                private final AnnotationMetadata metadata;
    
                private final String importClassName;
    
                public Entry(AnnotationMetadata metadata, String importClassName) {
                    this.metadata = metadata;
                    this.importClassName = importClassName;
                }
    
                /**
                 * Return the {@link AnnotationMetadata} of the importing
                 * {@link Configuration} class.
                 */
                public AnnotationMetadata getMetadata() {
                    return this.metadata;
                }
    
                /**
                 * Return the fully qualified name of the class to import.
                 */
                public String getImportClassName() {
                    return this.importClassName;
                }
    
                @Override
                public boolean equals(@Nullable Object other) {
                    if (this == other) {
                        return true;
                    }
                    if (other == null || getClass() != other.getClass()) {
                        return false;
                    }
                    Entry entry = (Entry) other;
                    return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName));
                }
    
                @Override
                public int hashCode() {
                    return (this.metadata.hashCode() * 31 + this.importClassName.hashCode());
                }
    
                @Override
                public String toString() {
                    return this.importClassName;
                }
            }
        }
    
    }
    

    2. 解析逻辑

    可以看到也是立即被实例化,且并未被注入容器,被放入一个 deferredImportSelectorHandler (可视为一个容器)
    org.springframework.context.annotation.ConfigurationClassParser#processImports

    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 {
            // ...
        }
    }
    

    3. deferredImportSelectorHandler.handle & DeferredImportSelectorHandler

    private class DeferredImportSelectorHandler {
    
        /**
         * 类似 BeanDefinitionHolder, 对 DeferredImportSelector 和 ConfigurationClass 封装一下
         */
        @Nullable
        private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
    
        public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
            DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
            // emm, 这是考虑到并发处理吗, 感觉好像也不对, 如果在 process 中, 则立即处理
            if (this.deferredImportSelectors == null) {
                // 实际上这里的逻辑和 process 是一致的
                DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                handler.register(holder);
                handler.processGroupImports();
            }
            else {
                // 否则添加等待批量处理
                this.deferredImportSelectors.add(holder);
            }
        }
    
        public void process() {
            List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
            this.deferredImportSelectors = null;
            try {
                if (deferredImports != null) {
                    // 这个应该是处理器
                    DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                    deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                    // 每一个进行 register
                    deferredImports.forEach(handler::register);
                    // 然后批量处理
                    handler.processGroupImports();
                }
            }
            finally {
                this.deferredImportSelectors = new ArrayList<>();
            }
        }
    }
    

    4. 实际处理流程 DeferredImportSelectorGroupingHandler

    由上面可看出先对每个 selector 执行 DeferredImportSelectorGroupingHandler#register,然后批量处理 DeferredImportSelectorGroupingHandler#processGroupImports
    暂略,因为还不知道 group 的作用

    5. 实际调用时机

    由上面可看出 handle 会将 DeferredImportSelector 暂时存储,还未执行。
    ConfigurationClassParser 处理配置类就是这个,而这个方法只会被 org.springframework.context.annotation.ConfigurationClassPostProcessor 调用,如果我们传入一个启动类,那么当这个启动类被处理完毕,才会执行 process。

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            try {
                // 一般是进入这里,其他的暂略
                if (bd instanceof AnnotatedBeanDefinition) {
                    parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                }
                else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                    parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
                }
                else {
                    parse(bd.getBeanClassName(), holder.getBeanName());
                }
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
            }
        }
    
        this.deferredImportSelectorHandler.process();
    }
    
  • 相关阅读:
    activity学习参考
    Activity工作流入门
    Mysql 命令查看函数,触发器。。
    本地redis下载并启动
    Could not initialize class com.taobao.diamond.client.impl.DiamondEnvRepo
    mysql把表(表已有数据)的某列属性由空到非空,以及常用列操作
    easyexcel导出两种方式response返回文件流下载和保存到服务器返回下载链接
    EasyExcel导出带下拉框,并解决导出之后打开总是显示发现不可读取内容
    el-table的fixed固定列属性导致数据错位
    el-input按回车时,解决同时触发回车和失焦事件的问题
  • 原文地址:https://www.cnblogs.com/chenxingyang/p/16124192.html
Copyright © 2020-2023  润新知