SpringBoot自动配置原理

SpringBoot现在基本是标配了,除非是老旧的项目,或者保守一点的企业,都会选择SpringBoot。

在SpringBoot出现之前,使用SSH或者SSM,都要很多的XML配置文件,而这些配置,在每个项目中,大部分都是相同的。

虽然都一样,但项目都要配置,可能会出现配置几小时,写代码几分钟的情况,把项目启动拖慢了。SpringBoot则是为了解决这种问题而生的,提高开发效率。

用过SpringBoot的小伙伴都知道,在IDEA使用SpringBoot Initializer,快速配置项目,写一个Controller就可以,快速搭建起Web项目。

SpringBoot给我们提供了大量的starter,里面已经帮我们配置了常用配置,如果我们需要改动,则在application.yml中配置即可。

SpringBoot之所以可以这样做,是因为它的设计策略,开箱即用和约定大于配置。

下面我们看下SpringBoot帮我们做了什么吧!

自动装配

要使用SpringBoot,我们需要指定parent父工程

基础配置

pom文件指定parent父工程

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

点进去会发现,spring-boot-starter-parent也有父工程,就是spring-boot-dependencies,继续点进去

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>

spring-boot-dependencies看来是管理依赖和版本号的,所以我们依赖第三方库时,如果在这个依赖列表中有,则不需要写版本号了

<properties>
    <activemq.version>5.15.10</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.76</appengine-sdk.version>
    <artemis.version>2.10.1</artemis.version>
    <aspectj.version>1.9.4</aspectj.version>
    <assertj.version>3.13.2</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.0.1</awaitility.version>
    <bitronix.version>2.1.4</bitronix.version>
    <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
    <byte-buddy.version>1.10.1</byte-buddy.version>
    ...太多了,省略其他
</properties>

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>2.2.0.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
        <version>2.2.0.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test-autoconfigure</artifactId>
        <version>2.2.0.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-actuator</artifactId>
        <version>2.2.0.RELEASE</version>
      </dependency>
    <dependencies>
    ...太多了,省略其他
<dependencyManagement>

我们的父工程spring-boot-starter-parent,还帮我们指定了配置文件的格式

<build>
    <resources>
      <resource>
        <filtering>true</filtering>
        <directory>${basedir}/src/main/resources</directory>
        <!-- 指定了配置文件的格式,加载顺序为yml => yaml => properties -->
        <includes>
          <include>**/application*.yml</include>
          <include>**/application*.yaml</include>
          <include>**/application*.properties</include>
        </includes>
      </resource>
      <resource>
        <directory>${basedir}/src/main/resources</directory>
        <excludes>
          <exclude>**/application*.yml</exclude>
          <exclude>**/application*.yaml</exclude>
          <exclude>**/application*.properties</exclude>
        </excludes>
      </resource>
    </resources>
<build>

启动器 starter

SpringBoot将每种使用场景所需要的依赖和依赖,封装成一个启动器starter,我们需要引入某种领域的功能时,直接依赖对应的starer即可。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>

例如我们常用的Web开发,需要依赖SpringMVC等,SpringBoot提供了spring-boot-starter-web启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

我们点进行该starter,他给我们定义了以下依赖:

  1. spring-boot-starter SpringBoot基础启动器
  2. spring-boot-starter-json json序列化、反序列化的启动器
  3. spring-boot-starter-tomcat 内嵌Tomcat
  4. spring-web和spring-webmvc,就是我们的SpringMVC
<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.2.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.2.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.2.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
      <version>2.2.0.RELEASE</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <artifactId>tomcat-embed-el</artifactId>
          <groupId>org.apache.tomcat.embed</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
</dependencies>

启动类 SpringBootApplication

SpringBoot要求我们提供一个启动类,并且类头加上 @SpringBootApplication注解,该注解就是SpringBoot启动的核心。

@SpringBootApplication
public class SpringbootEsApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootEsApplication.class, args);
        log.info("项目启动成功,访问地址:http://localhost:8081/");
    }
}

我们点进去@SpringBootApplication注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {
    //省略属性...
}

我们会发现SpringBootApplication是一个复合注解,当中最重要的是@SpringBootConfiguration@EnableAutoConfiguration,这2个注解。
@ComponentScan注解是包扫描,因为没有配置扫描包,默认是扫描标识该注解的类的包,以及它以下的子包,所以启动类一般在根包下。

  • @SpringBootConfiguration注解

我们发现@SpringBootConfiguration注解 ,最主要是加上了@Configuration注解。
我们知道@Configuration注解 就代表了一个JavaConfig方式的Spring的容器,所以我们启动器类也相当于一个容器。

SpringBootConfiguration注解没什么可看了,我们看下一个注解

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

@EnableAutoConfiguration注解中,主要注解是@Import(AutoConfigurationImportSelector.class)
@Import注解,帮我们导入了AutoConfigurationImportSelector这个类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>[] exclude() default {};

String[] excludeName() default {};
}
  • AutoConfigurationImportSelector类

AutoConfigurationImportSelector类实现了DeferredImportSelector接口,该接口继承ImportSelector接口 ,会要求复写selectImports()方法。

ImportSelector接口,主要是为了导入@Configuration配置的,而DeferredImportSelector是延期导入,当所有的@Configuration都处理完成后,再调用DeferredImportSelector进行处理。

所以AutoConfigurationImportSelector类是延迟导入的,所有@Configuration都处理完后,再调用它的selectImports()方法。

selectImports()方法,调用了getAutoConfigurationEntry()方法,而getAutoConfigurationEntry()又调用了getCandidateConfigurations()方法。
getCandidateConfigurations()方法是重点!

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        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 = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                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;
    }
}
  • getCandidateConfigurations()方法

方法中,调用SpringFactoriesLoader.loadFactoryNames(),传入2个参数,EnableAutoConfiguration的Class和Bean的ClassLoader。

loadFactoryNames()方法,返回一个集合,如果集合为空,进入下一句的Assert断言,就会抛出异常。

最后返回这个配置集合。

//AutoConfigurationImportSelector
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
            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;
}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

protected ClassLoader getBeanClassLoader() {
    return this.beanClassLoader;
}
  • SpringFactoriesLoader

loadFactoryNames()方法,获取了传入的EnableAutoConfiguration注解的Class,调用loadSpringFactories()方法。
loadSpringFactories()方法,会读取jar包中META-INF目录的spring.factories配置文件。

如果读取不到,则返回一个空集合。

public final class SpringFactoriesLoader {
    //jar包中的META-INF目录下,spring.factories配置文件
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
    
        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
}
  • spring.factories配置文件

我们选一个starter,例如spring-boot-autoconfigure,找到它的META-INF目录,找到spring.factories文件,打开。

我们发现文件里,配置了很多自动配置属性(内容有删减,实在太多了!)。
它的形式是Key-Value,例如其中一个Key是EnableAutoConfiguration的全类名,它的Value是好几个名字以AutoConfiguration结尾的类,每个类之间用逗号分隔。

刚才我们跟踪的loadFactoryNames()方法,传入的EnableAutoConfiguration的Class,就是要从spring.factories配置文件中找到它对应的那一组Value。

我们以ServletWebServerFactoryAutoConfiguration为例,点进去看一下

# 省略其他配置...

# Auto Configure !!!!!!!! 重点在这里 !!!!!!!!
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# 省略其他配置...
  • ServletWebServerFactoryAutoConfiguration类

我们看到该类的类头上,有@EnableConfigurationProperties注解,该属性表示加载配置属性,这里指定了一个ServerProperties类。

我们点进去ServerProperties类看一下

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    //...省略
}
  • ServerProperties类

这个是一个和配置信息相对应的类,它类头上配置了@ConfigurationProperties注解,它可以将配置文件中的配置项的内容,映射到我们的类的变量上。

注解上,配置的prefix属性,就代表了server.xxx系列配置,例如我们配置端口:server.port,该注解将我们的配置映射到ServerProperties上。

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    /**
     * Server HTTP port.
     */
    private Integer port;

    /**
     * Network address to which the server should bind.
     */
    private InetAddress address;
}

到此为止,自动配置的流程基本通了,总结一下:

SpringBoot启动类的main方法启动时,会找@EnableAutoConfiguration注解,而该注解就在@SpringBootApplication上。而@EnableAutoConfiguration注解上,使用了@Import注解,导入了AutoConfigurationImportSelector类。
而该类,会去找META-INF/spring.factories配置文件,这个配置文件中配置了一系列的以AutoConfiguration结尾的类,就是自动配置类。
而每个配置类,都有一个Properties结尾的配置类,它和我们在yml中的配置项时一一对应的,相当于绑定配置到了该对象中。

如果只是想面试了解一下,到这里就可以了,而如果更想深入,就要继续跟一下。

如果要继续跟,就还有一个疑点,自动装配是什么时候开始的呢,其实就是AutoConfigurationImportSelector类上的selectImports()方法,还不知道它什么会被调用。

何时开始进行自动装配

我们回归到Spring,Spring应用启动,会在AbstractApplicationContext类中,调用refresh()方法。

refresh()方法中,调用了invokeBeanFactoryPostProcessors()方法,该方法是用来处理BeanFactoryPostProcessor接口的,而BeanFactoryPostProcessor的有一个子接口BeanDefinitionRegistryPostProcessor

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        //省略无关代码...

        invokeBeanFactoryPostProcessors(beanFactory);

        //省略无关代码...
    }
}

//BeanDefinitionRegistryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
  • ConfigurationClassPostProcessor类

子接口BeanDefinitionRegistryPostProcessor,有一个实现类ConfigurationClassPostProcessor,它是专门处理@Configuration注解的。

processConfigBeanDefinitions()方法中,就是处理@Configuration注解的类。主要是使用ConfigurationClassParser类的parse()方法。

我们进去parse()方法,看一下

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

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        //省略部分代码        

        // Parse each @Configuration class
        ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, this.problemReporter, this.environment,
                this.resourceLoader, this.componentScanBeanNameGenerator, registry);

        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
        Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
        do {
            //解析处理@Configuration注解的类
            parser.parse(candidates);
            parser.validate();

            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);

            // Read the model and create bean definitions based on its content
            if (this.reader == null) {
                this.reader = new ConfigurationClassBeanDefinitionReader(
                        registry, this.sourceExtractor, this.resourceLoader, this.environment,
                        this.importBeanNameGenerator, parser.getImportRegistry());
            }
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);

            candidates.clear();
            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());
    }
}
  • ConfigurationClassParser类的parse()方法

首先类中有一个内部类DeferredImportSelectorHandler,构造方法ConfigurationClassParser实例时,就创建该内部类的实例。

parse()方法调用时,最后一句调用了processDeferredImportSelectors()方法。

class ConfigurationClassParser {
    public void parse(Set<BeanDefinitionHolder> configCandidates) {
            this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
        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);
            }
        }
        //重点
        processDeferredImportSelectors();
    }
}
  • processDeferredImportSelectors()方法

重点在String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());

调用的是DeferredImportSelectorHolder类,它保存了DeferredImportSelector的引用,在这个for循环中,调用了DeferredImportSelectorselectImports()方法,从而调用到了我们之前分析的AutoConfigurationImportSelector类中的selectImports()方法了。

private void processDeferredImportSelectors() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);

    for (DeferredImportSelectorHolder deferredImport : deferredImports) {
        ConfigurationClass configClass = deferredImport.getConfigurationClass();
        try {
            String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
            processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        }
    }
}

//该类,保存了配置类和DeferredImportSelector的引用
private static class DeferredImportSelectorHolder {
    private final ConfigurationClass configurationClass;

    private final DeferredImportSelector importSelector;

    public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
        this.configurationClass = configClass;
        this.importSelector = selector;
    }

    public ConfigurationClass getConfigurationClass() {
        return this.configurationClass;
    }

    public DeferredImportSelector getImportSelector() {
        return this.importSelector;
    }
}

参考资料

SpringBoot:认认真真梳理一遍自动装配原理

Spring Boot面试杀手锏 — 自动配置原理

深入理解SpringBoot之自动装配

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345