Spring源码 - DeferredImportSelector实现分析

目录

一、DeferredImportSelector类结构和接口调用时机

二、DeferredImportSelectorHandler的process方法

1、DeferredImportSelectorGroupingHandler结构

2、DeferredImportSelectorGroupingHandler的register方法

3、DeferredImportSelectorGroupingHandler的processGroupImports方法


    在分析DeferredImportSelector之前,根据名字Deferred(延迟的)ImportSelector。ImportSelector则是将selectImports返回的字符串数组,注册成为Bean,详细过程可以参见:Spring源码-ImportSelector实现分析。那么有几个问题:1)、怎么延迟的;2)、执行时机,或者在什莫时候被调用的;3)、返回后的字符串数组,怎么注册成Bean(或者Beandefinition)。

一、DeferredImportSelector类结构和接口调用时机

   之前在分析Spring源码时,并没有分析DeferredImportSelector类型,其两个官方的实现类AutoConfigurationImportSelectorImportAutoConfigurationImportSelector都是Spring Boot后新增的实现。

public interface DeferredImportSelector extends ImportSelector {
 
    default Class<? extends Group> getImportGroup() {
        return null;
    }
 
    interface Group {
 
        void process(AnnotationMetadata metadata, DeferredImportSelector selector);
 
        Iterable<Entry> selectImports();
 
        class Entry {
            private final AnnotationMetadata metadata;
            private final String importClassName;
 
            public Entry(AnnotationMetadata metadata, String importClassName) {
                this.metadata = metadata;
                this.importClassName = importClassName;
            }
        }
    }
}

    DeferredImportSelector的回调时机发生在ConfigurationClassPostProcessor处理阶段,但是在处理DeferredImportSelector类型时并没有回调从顶层接口ImportSelectorselectImports(AnnotationMetadata importingClassMetadata)方法,则按道理AutoConfigurationImportSelector(自动装配)的该接口只需要写一个空方法即可。

    执行该回调方法的先后顺序为 Group#process 和 Group#selectImports,回调时机为:

AbstractApplicationContext # refresh

AbstractApplicationContext # invokeBeanFactoryPostProcessors

PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors

PostProcessorRegistrationDelegate # invokeBeanDefinitionRegistryPostProcessors

ConfigurationClassPostProcessor # postProcessBeanDefinitionRegistry

ConfigurationClassPostProcessor # processConfigBeanDefinitions

ConfigurationClassParser # parse

ConfigurationClassParser$DeferredImportSelectorHandler # process           (下面都为代理对象处理)

ConfigurationClassParser$DeferredImportSelectorGroupingHandler # processGroupImports      

ConfigurationClassParser$DeferredImportSelectorGrouping # getImports

 

    之前在分析ConfigurationClassPostProcessor的处理过程的时候并没有注意到DeferredImportSelector类型的处理过程(详细可以参考:SpringIoc源码(十)- ApplicationContext(六)- refresh(ConfigurationClassPostProcessor上)),按照上面的调用trace继续分析,ConfigurationClassParser # parse方法,如下:

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();
}

    其中for循环之前分析过了,会将可能存在的Bean进行注入,并且继续进行解析肯能的注解情况,比如:有一个@Component标注的类,上面还有@Import等情况。而deferredImportSelectorHandler.process()则是对DeferredImportSelector类型的处理。

    看一下DeferredImportSelectorHandler deferredImportSelectorHandler的结构:

private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

    唯一的属性就是存放DeferredImportSelector类型的List,还有handle和process两个方法。而DeferredImportSelectorHolder的结构为:

private static class DeferredImportSelectorHolder {
    // 注解所在类
    private final ConfigurationClass configurationClass;
    // DeferredImportSelector类型,比如:AutoConfigurationImportSelector
    private final DeferredImportSelector importSelector;
}

     DeferredImportSelector的两个官方实现都是Spring Boot的,看到上面的结构理解起来比较抽象。在启动Spring Boot项目时,默认只会注入一对值:

configurationClass为被@SpringBootApplication标注的启动类;

DeferredImportSelector为AutoConfigurationImportSelector类型。

二、DeferredImportSelectorHandler的process方法

    继续deferredImportSelectorHandler.process()分析:

public void process() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            deferredImports.forEach(handler::register);
            handler.processGroupImports();
        }
    } finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

     1)、创建DeferredImportSelectorGroupingHandler对象,对DeferredImportSelectorHolder类型List进行排序,然后遍历调用DeferredImportSelectorGroupingHandler的register方法,将DeferredImportSelectorHolder数据set到DeferredImportSelectorGroupingHandler内。

    2)、调用hander的processGroupImports方法进行解析注册。

    3)、finally,将容器置为空。

 

1、DeferredImportSelectorGroupingHandler结构

    首先所有的解析过程交给DeferredImportSelectorGroupingHandler进行处理,先看看其内部结构。

private class DeferredImportSelectorGroupingHandler {

    private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

    private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
}

    有两个字段,通过register方法挨个进行register(相当于set方法)到上两个字段中, 最后统一调用processGroupImports方法进行处理。

    1)、configurationClasses比较清楚,存放的是注解信息和标记注解的类(比如:当前为@SpringBootApplication锁在的类的注解信息被封装为StrandardAnnotationMetadata)。

    2)、group中存储的key为DeferredImportSelector.Group或者DeferredImportSelectorHolder对象(因为是调用DeferredImportSelector的getImportGroup方法获取的,可能为null,所以key为Object类型)。value为DeferredImportSelectorGrouping类型,再看看其结构(还是比较清晰的,只是需要理解几层的关系):

private static class DeferredImportSelectorGrouping {

    private final DeferredImportSelector.Group group;

    private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
}

    

2、DeferredImportSelectorGroupingHandler的register方法

public void register(DeferredImportSelectorHolder deferredImport) {
    Class<? extends Group> group = deferredImport.getImportSelector()
            .getImportGroup();
    DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
            (group != null ? group : deferredImport),
            key -> new DeferredImportSelectorGrouping(createGroup(group)));
    grouping.add(deferredImport);
    this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getConfigurationClass());
}

    1)、调用DeferredImportSelector.Group的getImportGroup方法,获取返回的DeferredImportSelector.Group。

    2)、grouping的put方法,Group为null则设置传入的DeferredImportSelectorHolder 本身。value为new的DeferredImportSelectorHolder 类型。

    调用了createGroup方法,通过Class和反射创建对象;并且传入ConfigurationClassParser的environment、resourceLoader、registry对象,在反射创建完对象后对部分Aware方法进行了回调(BeanClassLoaderAware、BeanFactoryAware、EnvironmentAware、ResourceLoaderAware)。

    没搞懂为什么要回调,因为Bean的生命周期本身就会进行回调。后面debugger发现,AutoConfigurationImportSelector本身实现了那几个接口,AutoConfigurationImportSelector.AutoConfigurationGroup也实现了那几个接口。知道当前的调用时机还没有进行getBean操作,不能执行生命周期方法(回调,设置BeanFactory等),但是执行DeferredImportSelector的process和selectImports方法时可能需要使用到上面四个AbstractApplicationContext级别的内置对象,则在此处回调赋值。

    3)、上面先进行了put方法,返回了DeferredImportSelectorGrouping对象。再将DeferredImportSelectorHolder添加进去。

    4)、为List<DeferredImportSelectorHolder> deferredImports 添加值。

 

3、DeferredImportSelectorGroupingHandler的processGroupImports方法

public void processGroupImports() {
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = this.configurationClasses.get(
                    entry.getMetadata());
            try {
                processImports(configurationClass, asSourceClass(configurationClass),
                        asSourceClasses(entry.getImportClassName()), false);
            } catch (BeanDefinitionStoreException ex) {
                // 省略异常代码
            }
        });
    }
}

    主要有两步

1)、调用getImports方法获取Iterable<Group.Entry>

public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                deferredImport.getImportSelector());
    }
    return this.group.selectImports();
}

    之前做了很多准备工作,当前需要通过调用DeferredImportSelector.Group的process和selectImports方法,返回Iterable<DeferredImportSelector.Group.Entry> 类型对象,后续调用统一的processImports方法进行处理。

 

2)、遍历调用ConfigurationClassParser的processImports方法,将返回的Class类型进行解析并注册成Bean(如果该Class上有其他注解,也会递归进行解析注册完成,比如该类上还有@ComponentScan注解),可以参考Spring源码-ImportSelector实现分析

 

©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值