吊炸天的SpringBoot自动装配原理剖析(附面试总结)

在SpringBoot中,不得不说的一个点就是自动装配,它是starter的基础,也是SpringBoot的核心,那么什么是自动装配呢?showTime

 

自动装配在SpringBoot中是通过@EnableAutoConfiguration的注解来实现的,而这个注解的声明是在复合注解  @SpringBootApplication中

吊炸天的SpringBoot自动装配原理剖析(附面试总结)_第1张图片

 

开始正餐之前先给大家说下其他注解


@SpringBootConfiguration  是一个声明当前类是boot启动类,是一个复合注解



@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

正餐开始 @EnableAutoConfiguration,跟踪源码点进去如下

吊炸天的SpringBoot自动装配原理剖析(附面试总结)_第2张图片

当大家看到@AutoConfigurationPackage这个注解的时候是不是就豁然开朗,这就是为什么Spring扫描启动类当前所在包及子包下的所有组件

 

大家如果经常阅读源码的话会看见@import和@Conditional这两个注解,本节遇到了@import这个注解,我就先说明这个注解的作用:导入类。

@Import({AutoConfigurationImportSelector.class})

大家注解@import它导入的这个配置类是一个叫 AutoConfigurationImportSelector 这个一个配置类,当然就目前而言,不管这个类是做什么的,大家一定得清楚就是它一定会实现配置类的导入,至于具体导入的方式和@Configuration有什么区别,就是下面我要说明的

AutoConfigurationImportSelector这个类最终实现了ImportSelector,ImportSelector是一个接口,里面有一个抽象方法,返回的是一个String数组,在这个数组中可以指定需要装配到IOC容器的类,当在@Import中导入它的实现类侯,就会把该类中返回的Class名称都装载到IOC容器中

吊炸天的SpringBoot自动装配原理剖析(附面试总结)_第3张图片

基于前面的分析,大家可以猜想到,自动装配的核心是扫描约定目录下的文件进行解析,解析后将得到的Configuration配置类通过ImportSelector进行导入,完成Bean的自动装配。那么我们下面仔细分析下AutoConfigurationImportSelector中的selectImport方法

AutoConfigurationImportSelector主要有两个功能:

1.从META-INF/spring-autoconfigure-metadata.properties中加载自动装配的条件元数据,简单来说就是只有满足条件的Bean才能够进行装配

2.收集所有符合条件的配置类  antoConfigurationEntry.getConfiguration(),完成装配

 

吊炸天的SpringBoot自动装配原理剖析(附面试总结)_第4张图片

吊炸天的SpringBoot自动装配原理剖析(附面试总结)_第5张图片

马上就是核心加载过程了:看我的标记,下面介绍此方法

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);





   最最核心过程
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            





return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

 

当你看到这,其实你已经了解了大半,就剩下最后一个Boss需要你KO

----------------------------------------------------------------------------------------------------------------------------------------------------------------

 






protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {

            //获得@EnableAutoConfiguration注解中的属性exclude  excludeName等
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List configurations = 
//获得所有自动装配的配置类
this.getCandidateConfigurations(annotationMetadata, attributes);

             //去除重复的配置项
            configurations = this.removeDuplicates(configurations);
            //根据@EnableAutoConfiguration注解中配置的exclude等属性,把不需要的自动装配的类去除
            Set exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            //广播事件
this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

 

总结:就是它获取所有的配置类,通过去重、exclude排除等操作,得到最终需要实现自动装配的配置类。这里需要关注的就是getCandidateConfigurations 他是配置类 最核心 最核心 最核心 最核心

我说了4遍哦,的方法

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

这里面用到了SpringFactoriesLoader,它是Spring内部提供的一种加载方式,就跟SPI 一样,(SPI不懂的大家百度下,jdk spi  dubbox spi)。简单来说就是扫描classpath下的META-INF/spring.factories文件,其中spring.factories文件中的数据以key=value格式存储,而这个加载器会根据key到到对应的value.吊炸天的SpringBoot自动装配原理剖析(附面试总结)_第6张图片

 

作为一个帅气的boy我很贴心如意的给大家准备了面试答案哦

通过@Import(AutoConfigurationImportSelector)实现配置类的导入,但是并不是传统意义上的单个导入,而是批量

AutoConfigurationImportSelector类实现了ImportSelector接口,重写了方法selectImports,用于实现批量装配

通过spirng tigao de SpringFactoriesLoader机制,扫描classpath路径下的META-INF/spring.factories 读取需要实现自动装配的配置类。

最终通过筛选的方式,把不符合的类提出,最终完成SpringBoot的自动装配

你可能感兴趣的:(spring,SpringBoot2,SpringBoot自动装配,SpringBoot2面试,源码分析)