第六十章 Spring之假如让你来写Boot——自动装配篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇

第三部分——事务篇

第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇

第四部分——MVC篇

第三十三章 Spring之梦开始的地方——MVC
第三十四章 Spring之假如让你来写MVC——草图篇
第三十五章 Spring之假如让你来写MVC——映射器篇
第三十六章 Spring之假如让你来写MVC——拦截器篇
第三十七章 Spring之假如让你来写MVC——控制器篇
第三十八章 Spring之假如让你来写MVC——适配器篇
第三十九章 Spring之假如让你来写MVC——番外篇:类型转换
第四十章 Spring之假如让你来写MVC——ModelAndView篇
第四十一章 Spring之假如让你来写MVC——番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC——视图篇
第四十三章 Spring之假如让你来写MVC——上传文件篇
第四十四章 Spring之假如让你来写MVC——异常处理器篇
第四十五章 Spring之假如让你来写MVC——国际化篇
第四十六章 Spring之假如让你来写MVC——主题解析器篇
第四十七章 Spring之假如让你来写MVC——闪存管理器篇
第四十八章 Spring之假如让你来写MVC——请求映射视图篇
第四十九章 Spring之假如让你来写MVC——番外篇:属性操作
第五十章 Spring之假如让你来写MVC——融入IOC容器篇
第五十一章 Spring之源码阅读——MVC篇

第五部分——Boot篇

第五十二章 Spring之再进一步——Boot
第五十三章 Spring之假如让你来写Boot——环境篇
第五十四章 Spring之假如让你来写Boot——注解篇(上)
第五十五章 Spring之假如让你来写Boot——注解篇(下)
第五十六章 Spring之假如让你来写Boot——SPI篇
第五十七章 Spring之假如让你来写Boot——配置文件篇(上)
第五十八章 Spring之假如让你来写Boot——配置文件篇(下)
第五十九章 Spring之假如让你来写Boot——番外篇:再谈Bean定义
第六十章 Spring之假如让你来写Boot——自动装配篇
第六十一章 Spring之假如让你来写Boot——番外篇:杂谈Starter
第六十二章 Spring之假如让你来写Boot——番外篇:重构BeanFactory
第六十三章 Spring之假如让你来写Boot——番外篇:再谈ApplicationContext
第六十四章 Spring之假如让你来写Boot——内嵌Web容器篇
第六十五章 Spring之假如让你来写Boot——Main方法启动篇
第六十六章 Spring之最终章——结语篇



前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


     书接上回,在上篇 第五十八章 Spring之假如让你来写Boot——配置文件篇(下) 中,A君 已经支持了Yaml配置文件。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大 要求 A君在一周内开发个简单的 IOC容器

    前情提要:A君 已经支持了Yaml配置文件。。。

四十九版 自动装配

     办公室内,老大 看向 A君,缓缓开口问道:“好了,A君,现在前置工作都已经准备好了,接下来就要开始着手实现核心功能了——自动装配。对此你有什么思路吗?”

    A君 略一沉吟,回答道:“其实所谓 自动装配 就分为两部分内容:怎么找到?怎么加载?就拿连接池举例,用户只要配置好数据的信息,框架就应该自动创建连接池对象,用户就可以直接使用了。用户虽然简单了,但是框架却犯难了,它怎么知道用户要用哪个连接池,C3P0?还是Druid?连接池还只是冰山一角,市面上那么多开源框架,不可能一一实现,所以老样子,提供规范,让开源框架自己实现,就凭咱这影响力,不怕它们不就范,嘿嘿。这是自动装配的第一步:由框架提供规范,也就是所谓的 SPI 机制,这个在前面已经提到过了。接下来就是如何加载的问题了,这一步也比较好理解,毕竟 SPI 加载过来的是类全路径,那么肯定得先加载并解析这个类,当然了,某些框架可能需要创建多个Bean,这时候写一大堆配置类也不合适,最好是需要的Bean都在一个配置类里,这样大家都方便,也不会造成配置文件的冗余。只要能把这些信息注册到 容器 中,自动装配 也就完事了!”

    听完 A君 的陈述,老大 露出了赞许的目光:“不错,确实是如此,A君 你已经成长起来了。很多技术其实也没有那么神奇,仔细思索下,好像也只能这么干。既然你已经理解了,那我就不再多说什么了,你自己看着做吧。”

    在回到位置上之后,A君 一阵琢磨,虽然在会上侃侃而谈,但真正到实现的时候,又有点懵逼。思来想去,A君 问了自己几个问题,只要把问题梳理出来,才能有办法解决:

  1. 自动装配说到底也是个配置类,如何区别配置类和正常类?
  2. 配置类如果需要满足一定条件怎么办?
  3. 配置类如果存在依赖怎么办?
  4. 找到配置类后,如何根据方法注册Bean

果然,随着问题的不断提出,A君 的思路也清晰了起来。只要解决了对应的问题,自动配置就能逐步实现。对于这几个问题,解决的方式其实并不难:

如何区别配置类和正常类

    这个问题简单,只需新增一个注解用以区分即可。A君 定义@Configuration注解,用以区别配置类。代码如下:

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

    @AliasFor(annotation = Component.class)
    String value() default "";

    boolean proxyBeanMethods() default true;
}

这是简单的实现方式,简单也单薄。为此,A君 在新增一个注解,加点料,比如说:前后处理方法。@AutoConfiguration代码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore
@AutoConfigureAfter
public @interface AutoConfiguration {

    @AliasFor(annotation = Configuration.class)
    String value() default "";

    @AliasFor(annotation = AutoConfigureBefore.class, attribute = "value")
    Class<?>[] before() default {};

    @AliasFor(annotation = AutoConfigureBefore.class, attribute = "name")
    String[] beforeName() default {};

    @AliasFor(annotation = AutoConfigureAfter.class, attribute = "value")
    Class<?>[] after() default {};

    @AliasFor(annotation = AutoConfigureAfter.class, attribute = "name")
    String[] afterName() default {};
}
配置类需要满足一定条件

    配置类如果需要满足一定条件才能生效的话,什么条件?不知道,那就定义个接口吧,具体实现留给用户来做。A君 新增Condition类,代码如下:

/**
 * 配置生效条件接口
 */
@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

Condition接口控制粒度相对来说比较粗一点,也不难理解,某些情况下,用户需要排除整个配置类,而有些情况下,用户只想排除配置类下的某个Bean。为此,A君 提供了更细粒度的接口。ConfigurationCondition代码如下:

public interface ConfigurationCondition extends Condition {

    /**
     * 获取评估类型
     *
     * @return
     */
    ConfigurationPhase getConfigurationPhase();


    /**
     * 评估条件的各种配置阶段
     */
    enum ConfigurationPhase {

        /**
         * 如果条件不满足,整个配置类都不会被纳入容器,也就是说在解析阶段就会排除该配置
         */
        PARSE_CONFIGURATION,

        /**
         * 在注册 Bean 阶段评估条件,此时配置类已经被解析。条件只会影响具体 Bean 的注册,不会阻止整个配置类被加载
         */
        REGISTER_BEAN
    }
}

接下来还需要定义个注解配合使用,A君 定义@Conditional注解,代码如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

配置类存在依赖

    配置类如果存在依赖的话,怎么依赖那是用户的事,框架只需要提供注解即可。相比于@Conditional,导入就没有那么多限制了,只要是个正常的对象,就可以被注入。A君 新增@Import注解,代码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}

不管什么样的自动化,都应该存在着过滤,不然无法应对复杂的业务场景。和之前几个注解一样,@Import也得存在过滤。A君 新增AutoConfigurationImportFilter接口,代码如下:

/**
 * 自动导入过滤
 */
@FunctionalInterface
public interface AutoConfigurationImportFilter {

    boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata);

}

这么一来,用户是简单了,A君 却开始犯难了:用户配置好之后,要怎么处理呢?虽然@Import导入没有要求用户必须要实现某个接口,但是在实际的环境下,要根据什么条件,什么场景进行导入?导入后Bean又要怎么定义?这一大堆的东西想的 A君 头都大了。幸运的是,起码有一件事可以确实:那就是定义接口。A君 新增ImportSelector接口,用以选择导入时类,代码如下:

/**
 * 导入选择器,配置类处理完成前执行
 */
public interface ImportSelector {

    /**
     * 扫描对应的导入类
     *
     * @param importingClassMetadata
     * @return
     */
    String[] selectImports(AnnotationMetadata importingClassMetadata);

    default Predicate<String> getExclusionFilter() {
        return null;
    }
}

只是ImportSelector还是不够,因为ImportSelector执行时机是在配置类执行完成之前,而有些情况下,是要在配置类执行完成之后再执行导入,这时候就需要对ImportSelector进行拓展了。A君 新增DeferredImportSelector接口,代码如下:

/**
 * 延迟导入
 */
public interface DeferredImportSelector extends ImportSelector {

    /**
     * 获取导入类组
     *
     * @return
     */
    default Class<? extends Group> getImportGroup() {
        return null;
    }

    interface Group {

        /**
         * 分组
         *
         * @param metadata
         * @param selector
         */
        void process(AnnotationMetadata metadata, DeferredImportSelector selector);

        /**
         * 迭代器
         *
         * @return
         */
        Iterable<Entry> selectImports();

        class Entry {
            /**
             * 注解信息
             */
            private final AnnotationMetadata metadata;
            /**
             * 导入类名
             */
            private final String importClassName;

            //省略其他代码。。。
    }

}

接口定义好了之后,用户就可以通过实现ImportSelector接口,来自由拓展自己想要加载的类了。当然了,自动配置也属于其中的一个,那就先给自动配置整个吧。自动配置说来说去还是那么点东西:通过 SPI 扫描到对应的类,然后进行过滤即可。A君 新增AutoConfigurationImportSelector类,代码如下:

/**
 * 自动装配导入选择
 */
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

    private ConfigurableListableBeanFactory beanFactory;

    private Environment environment;

    private ClassLoader beanClassLoader;

    private ResourceLoader resourceLoader;

    private ConfigurationClassFilter configurationClassFilter;

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        /**
         * 是否开启当前导入配置
         */
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        /**
         * 1、获取注解配置
         * 2、删除所有重复配置
         * 3、删除所有排除配置
         * 4、过滤配置
         * 5、触发导入事件
         */
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    protected boolean isEnabled(AnnotationMetadata metadata) {
        if (getClass() == AutoConfigurationImportSelector.class) {
            return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
        }
        return true;
    }


    /**
     * 加载所有自动配置
     *
     * @param metadata
     * @param attributes
     * @return
     */
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = new ArrayList<>(
                SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
        ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
        return configurations;
    }


    private static class ConfigurationClassFilter {
		
        private final AutoConfigurationMetadata autoConfigurationMetadata;
        private final List<AutoConfigurationImportFilter> filters;

		
        List<String> filter(List<String> configurations) {
            String[] candidates = StringUtils.toStringArray(configurations);
            boolean skipped = false;
            for (AutoConfigurationImportFilter filter : this.filters) {
                boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
                for (int i = 0; i < match.length; i++) {
                    if (!match[i]) {
                        candidates[i] = null;
                        skipped = true;
                    }
                }
            }
            if (!skipped) {
                return configurations;
            }
            List<String> result = new ArrayList<>(candidates.length);
            for (String candidate : candidates) {
                if (candidate != null) {
                    result.add(candidate);
                }
            }
            return result;
        }

    }

    private static class AutoConfigurationGroup
            implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {

        private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();

        private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

        private ClassLoader beanClassLoader;

        private BeanFactory beanFactory;

        private ResourceLoader resourceLoader;

        private AutoConfigurationMetadata autoConfigurationMetadata;

        @Override
        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }

        @Override
        public Iterable<Entry> selectImports() {
            if (this.autoConfigurationEntries.isEmpty()) {
                return Collections.emptyList();
            }
            Set<String> allExclusions = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
            Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
                    .collect(Collectors.toCollection(LinkedHashSet::new));
            processedConfigurations.removeAll(allExclusions);

            return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
                    .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
                    .collect(Collectors.toList());
        }
    }

    /**
     * 自动配置包装类
     */
    protected static class AutoConfigurationEntry {
		/**
         * 配置类
         */
        private final List<String> configurations;
		/**
         * 排除类
         */
        private final Set<String> exclusions;
    }

}

    除了导入时候的要求,还有可能对Bean的信息有些要求,具体还是得用户去规定,所以老样子,A君 只要提供接口,新增ImportBeanDefinitionRegistrar类,代码如下:

/**
 * 导入bean的信息
 */
public interface ImportBeanDefinitionRegistrar {
    /**
     * 注册bean,定义名称生成器
     *
     * @param importingClassMetadata
     * @param registry
     * @param importBeanNameGenerator
     */
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
                                         BeanNameGenerator importBeanNameGenerator) {

        registerBeanDefinitions(importingClassMetadata, registry);
    }

    /**
     * 注册bean
     *
     * @param importingClassMetadata
     * @param registry
     */
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }

}

解析并注册Bean
定义Bean方法注解

    经过前面的层层铺垫,只剩最后一个问题了,也是最关键的一步了。接下来就是解析配置类,组装成BeanDefinition,注册到容器中了。在此之前,先要在解决个事,之前提到过,配置类不宜太多,之前以类级别的Bean显然就不适用了。所以这里 A君 添加个方法级注解,新增@Bean注解,代码如下:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
延迟导入

    好了,现在开始进入正题了,把配置类解析成一个对象,然后注册到容器中。先做第一步:解析配置类。说是解析配置类,其实就是读取配置类、方法、属性上的注解信息。这样一来涉及到的内容就非常多了,这么多内容全部黏贴出来,估计也看不下去,想到今天的任务,A君 就先从解析@Import注解切入,理解了一个注解的解析方式,其他都是类似的

    前文已经提到过,在添加@Import时,有个特殊的情况,那就是导入类实现了ImportSelector接口,而单单只是这样,还是没什么,无非递归调用就是了。但是别忘了,ImportSelector有个子接口——DeferredImportSelector。这位老兄特殊的地方在于:延迟加载,也就是等到配置类上的其他注解都解析完了,它才慢悠悠的解析。在正常的解析过程中,根本无法保证注解出现的顺序,在这种情况下,一般会把这些个对象先存起来,等到整个配置类都解析完了,在开始循环解析,DeferredImportSelector同样也不例外。那就先定义几个类来保存它的结构吧,A君 新增DeferredImportSelectorHolder类,用以保存配置类和DeferredImportSelector实现类的关系,代码如下:

/**
  * 保存配置类和延迟处理类的映射关系
*/
private static class DeferredImportSelectorHolder {
   //配置类
   private final ConfigurationClass configurationClass;
   //延迟处理类
   private final DeferredImportSelector importSelector;
	//省略其他方法。。。
}

由于是延迟加载,指不定谁在谁后呢?所以DeferredImportSelector多出了个组的概念,就像平时坐飞机的时,航空公司会根据座位等级或登机顺序将乘客分成几个组,每个组统一安排登机。这里的分组也是同理,那么接下来自然就是对其组的管理了。A君 新增DeferredImportSelectorGrouping类,代码如下:

/**
 * 延迟处理类分组
 */
private static class DeferredImportSelectorGrouping {
    /**
     * 分组
     */
    private final DeferredImportSelector.Group group;
    /**
     * 组内延迟处理类
     */
    private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();

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

组有了,还需要个‘工作人员’将乘客安排到各个组内去,安排其有序登机才行。这个‘工作人员’就是——DeferredImportSelectorGroupingHandler,代码如下:

 /**
 * DeferredImportSelector归类分组
 */
private class DeferredImportSelectorGroupingHandler {

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

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

    public void register(DeferredImportSelectorHolder deferredImport) {
        //获取组
        Class<? extends DeferredImportSelector.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());
    }

    public void processGroupImports() {
        for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
            Predicate<String> exclusionFilter = grouping.getCandidateFilter();
            grouping.getImports().forEach(entry -> {
                ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
                //处理导入
                processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                            Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                            exclusionFilter, false);
            });
        }
    }   
}

好了,现在还需要个总入口,来一键完成分组登机的动作。A君 新增DeferredImportSelectorHandler类,代码如下:

/**
 * DeferredImport处理类
 */
private class DeferredImportSelectorHandler {
    private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

    public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {

        DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
        /**
         * 处理阶段,会将该变量置为null
         */
        if (this.deferredImportSelectors == null) {
            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.forEach(handler::register);
                //开始导入
                handler.processGroupImports();
            }
        } finally {
            this.deferredImportSelectors = new ArrayList<>();
        }
    }
}

现在整个延迟加载的逻辑基本就已经定义好了,接下来只需要正常解析配置类,并把DeferredImportSelector放到最后处理就行了,A君 新增ConfigurationClassParser类,代码如下:

/**
 * 配置类转换
 */
public class ConfigurationClassParser {
	/**
     * 开始解析BD上的注解
     *
     * @param configCandidates
     */
    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            /**
             * 解析注解配置
             */
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            } else if (bd instanceof AbstractBeanDefinition && bd.hasBeanClass()) {//解析常规BD
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            } else {//其他
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        //执行延迟注入
        this.deferredImportSelectorHandler.process();
    }
    
	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                                Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
                                boolean checkForCircularImports) {

        //导入配置为空
        if (importCandidates.isEmpty()) {
            return;
        }
            
        for (SourceClass candidate : importCandidates) {
            /**
             * 导入类是否实现ImportSelector
             */
            if (candidate.isAssignable(ImportSelector.class)) {
                //加载对应类
                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
                if (selector instanceof DeferredImportSelector) {
                    this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                } else {
                    //加载对应的类
                    String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                    Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                    //递归处理
                    processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                }
            } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {//是否实现ImportBeanDefinitionRegistrar
                Class<?> candidateClass = candidate.loadClass();
                ImportBeanDefinitionRegistrar registrar =
                        ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                this.environment, this.resourceLoader, this.registry);
                //添加到Import注册表中
                configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            } else {
                //递归处理
                processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
            }
        }
    }
}
注册BD

    现在配置类该解析的也解析了,该保存的也保存了,剩下的就是将解析到的Bean注册到容器中就完事了。这个过程相当的简单,一个循环就可以搞定了。A君 新增ConfigurationClassPostProcessor类,代码如下:

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
        PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

	/**
     * 开始处理 @Configuration 注解
     *
     * @param registry
     */
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        //获取所有的bd
        String[] candidateNames = registry.getBeanDefinitionNames();
        List<BeanDefinitionHolder> configCandidates = findAllConfigurations(registry, candidateNames);

        //没有Configuration注解则返回
        if (configCandidates.isEmpty()) {
            return;
        }
        /**
         * 进行排序
         */
        sortConfiguration(configCandidates);
        SingletonBeanRegistry sbr = initRegistryAndEnv(registry);

        /**
         * 解析Configuration注解
         */
        ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, null, this.environment,
                this.resourceLoader, this.componentScanBeanNameGenerator, registry);

        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
        Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
        do {
        	//解析配置类
            parser.parse(candidates);
            /**
             * 符合条件的Configuration
             */
            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);

            if (this.reader == null) {
                this.reader = new ConfigurationClassBeanDefinitionReader(
                        registry, this.sourceExtractor, this.resourceLoader, this.environment,
                        this.importBeanNameGenerator, parser.getImportRegistry());
            }
            /**
             * 将Configuration声明的类转成BD
             */
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);
            //    processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

            candidates.clear();
            /**
             * 如果BD有所增加
             */
            if (registry.getBeanDefinitionCount() > candidateNames.length) {
                String[] newCandidateNames = registry.getBeanDefinitionNames();
                Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
                Set<String> alreadyParsedClasses = new HashSet<>();
                for (ConfigurationClass configurationClass : alreadyParsed) {
                    alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                }
                for (String candidateName : newCandidateNames) {
                    //将新增的对象添加到集合中
                    if (!oldCandidateNames.contains(candidateName)) {
                        BeanDefinition bd = registry.getBeanDefinition(candidateName);
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                                !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                            candidates.add(new BeanDefinitionHolder(bd, candidateName));
                        }
                    }
                }
                candidateNames = newCandidateNames;
            }
        }
        while (!candidates.isEmpty());

        // 将ImportRegistry注册为bean以支持ImportAware @Configuration类
        if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
            sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
        }

        if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
            ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
        }
    }
}

测试

    捣鼓了大半天,终于可以进入测试了。A君 先指定一个主类,让他拥有相应的注解。新增MainApplication类,代码如下:

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.hqd.ch03.test.boot.v49",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
                @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)})
public class MainApplication {
    public static void main(String[] args) {
        System.out.println(8888);
    }
}

注解瞅着是不是有点多,嘿嘿,可以把他们合成一个注解,那就是——@SpringBootApplication,这就是后话了。。。A君 将扫描路径定死在com.hqd.ch03.test.boot.v49,然后去别的路径新增一个配置类,MyConfig代码如下:

@Configuration
@Import(Object.class)
public class MyConfig {
    @Bean
    public Object obj() {
        return new Object();
    }
}

配置类中有个obj对象,如果自动注入成功了,obj就会出现在容器中,在此之前,别忘了配置下spring.factories。改动如下:

com.hqd.ch03.v49.boot.autoconfigure.EnableAutoConfiguration=\
com.hqd.ch03.test.autocinfig.MyConfig

接下来就可以编写测试代码了,如下:

 	@Test
    public void v49() throws NoSuchMethodException, IOException {
        System.out.println("############# 第四十九版: 自动装配篇 #############");
        com.hqd.ch03.v49.registry.BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
        AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(MainApplication.class);
        bdr.registerBeanDefinition("mainApplication", beanDefinition);
        //  AnnotatedBeanDefinitionReader abdr = new AnnotatedBeanDefinitionReader(bdr);
        // abdr.register(beanDefinition.getBeanClass());
        ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor();
        ccpp.processConfigBeanDefinitions(bdr);
        for (String beanDefinitionName : bdr.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName + ":" + bdr.getBeanDefinition(beanDefinitionName).getBeanClassName());
        }
    }

测试结果如下:

在这里插入图片描述

可以看到,obj对象已经顺利的出现在控制台了。说明 A君 的努力并没有白费。自动装配总算也完事了,今天可以睡个好觉了。下班。。。。

在这里插入图片描述


总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穷儒公羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值