java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > DeferredImportSelector实现

Spring中的DeferredImportSelector实现详解

作者:it_lihongmin

这篇文章主要介绍了Spring中的DeferredImportSelector实现详解,两个官方的实现类AutoConfigurationImportSelector和ImportAutoConfigurationImportSelector都是Spring Boot后新增的实现,需要的朋友可以参考下

前言

在分析DeferredImportSelector之前,根据名字Deferred(延迟的)ImportSelector。

ImportSelector则是将selectImports返回的字符串数组,注册成为Bean

那么有几个问题:

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类型时并没有回调从顶层接口ImportSelector的selectImports(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类型的处理过程

按照上面的调用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中的DeferredImportSelector实现详解的文章就介绍到这了,更多相关DeferredImportSelector实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文