3.【SpringBoot源码】SpringBoot自动配置原理

目录

一、简介

1)、@SpringBootConfiguration

2)、@ComponentScan

3)、@EnableAutoConfiguration

二、@AutoConfigurationPackage

三、@Import(AutoConfigurationImportSelector.class)

1)、AutoConfigurationImportSelector介绍

2)、自动配置流程

2-1)、ConfigurationClassParser#parse()

2-2)、this.deferredImportSelectorHandler.process()

2-2-1)、第一步:register()

2-2-2)、第二步:processGroupImports()


本篇源码基于spring-boot-2.1.0.RELEASE版本进行分析,各个版本可能存在一些差别。

一、简介

SpringBoot的启动从main()方法开始:

@SpringBootApplication
public class SampleTomcatApplication {

	public static void main(String[] args) {
		SpringApplication.run(SampleTomcatApplication.class, args);
	}

}

在主启动类上,我们只需要加上@SpringBootApplication注解,就能成功启动springboot应用了,那么我们就从注解入手,看下它是如何帮我们配置好所需要的一些环境的。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// @SpringBootConfiguration内部使用@Configuration修饰,标记启动类是一个Spring的配置类
@SpringBootConfiguration
// @EnableAutoConfiguration: 实现自动装配的核心注解. 内部包含两个注解:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)
@EnableAutoConfiguration
// 包扫描:扫描启动类所在的包以及子包所有Bean组件并注册到IOC容器中
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
// SpringBoot项目启动的核心注解
// @SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
public @interface SpringBootApplication {

    /**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
    // 等同于EnableAutoConfiguration注解的exclude属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    /**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
    // 等同于EnableAutoConfiguration注解的excludeName属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    /**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
    // 等同于ComponentScan注解的basePackages属性
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    /**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
    // 等同于ComponentScan注解的basePackageClasses属性
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

}

@SpringBootApplication 使用三个注解修饰:

1)、@SpringBootConfiguration

标记启动类是一个Spring的配置类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
// 相当于@SpringBootConfiguration就是@Configuration
public @interface SpringBootConfiguration {

}

2)、@ComponentScan

扫描启动类所在的包以及子包所有Bean组件并注册到IOC容器中。

3)、@EnableAutoConfiguration

实现自动装配的核心注解,内部包含两个注解:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自动配置包
@AutoConfigurationPackage
// 使用@Import注解导入AutoConfigurationImportSelector类,实现了ImportSelector接口,重写了selectImports()方法,帮助我们返回所有需要被注册为bean的类全限定类名的数组集合
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * 排除特定的自动配置类,使它们永远不会被应用
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * 排除特定的自动配置类名称,使其永远不会被应用
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

springboot的自动装配就是依靠@EnableAutoConfiguration这个注解实现的,而@EnableAutoConfiguration又是由下面两部分组成:

1、@AutoConfigurationPackage

2、@Import(AutoConfigurationImportSelector.class)

二、@AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 向容器中导入了AutoConfigurationPackages.Registrar组件
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

可以看到,@AutoConfigurationPackage注解的功能是由@Import注解实现的,它是Spring框架的底层注解,它的作用就是给容器中导入某个组件类AutoConfigurationPackages.Registrar,我们看下这是个什么组件?

@AutoConfigurationPackage注解的作用是将添加该注解的类所在的package作为自动配置package进行管理。也就是说当SpringBoot应用启动时默认会将启动类所在的package作为自动配置的package,这样主程序类所在包及所有子包下的组件就会被扫描到spring容器中。 

// 实现了ImportBeanDefinitionRegistrar、DeterminableImports接口,重写了registerBeanDefinitions()、determineImports()方法
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    // 注册bean定义
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        register(registry, new PackageImport(metadata).getPackageName());
    }

    // 返回一组需要注入的对象
    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImport(metadata));
    }

}

AutoConfigurationPackages.Registrar实现了ImportBeanDefinitionRegistrar,并重写了registerBeanDefinitions()注册bean定义信息的方法,这样在刷新IOC容器,即执行refresh()方法,在invokeBeanFactoryPostProcessors(beanFactory)这一步,会执行BeanDefinitionRegistryPostProcessor这个后置处理器的增强方法时,执行loadBeanDefinitions()加载bean定义信息,就会扫描到AutoConfigurationPackages.Registrar这个类,然后就会回调AutoConfigurationPackages.Registrar中的registerBeanDefinitions()方法:

register(registry, new PackageImport(metadata).getPackageName());

// 注册自动包规则的bean定义信息
public static void register(BeanDefinitionRegistry registry, String... packageNames) { // sample.tomcat
    // 判断bean定义信息注册表中是否存在org.springframework.boot.autoconfigure.AutoConfigurationPackages
    if (registry.containsBeanDefinition(BEAN)) {
        // 如果该bean已经注册,则将要注册包名称添加进去
        BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
        ConstructorArgumentValues constructorArguments = beanDefinition
                .getConstructorArgumentValues();
        constructorArguments.addIndexedArgumentValue(0,
                addBasePackages(constructorArguments, packageNames));
    }
    else {  // 如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去
        // 创建简单类型的bean定义信息GenericBeanDefinition
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        // bean类 : AutoConfigurationPackages$BasePackages
        beanDefinition.setBeanClass(BasePackages.class);
        // bean实例构造函数第一个参数 : 被使用类所在包名 (比如@SpringBootApplication注解所在类的包名)
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
                packageNames);
        // bean 角色 : 基础设施
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        // 注册bean定义信息, bean名称 : org.springframework.boot.autoconfigure.AutoConfigurationPackages
        registry.registerBeanDefinition(BEAN, beanDefinition);
    }
}

在本例中,SampleTomcatApplication在sample.tomcat包中,所以自动配置package为sample.tomcat。

@AutoConfigurationPackage和@ComponentScan的作用有什么区别?

  • @AutoConfigurationPackage :是为了给组件开发者使用的,组件开发者在一个路径下面有多个自动配置的类想加载,这个注解就不用对每个类单独添加 @Import 了,直接引入包路径更方便。
  • @ComponentScan :是为了用组件的开发者准备的,方便你对包路径进行自定义,比如你的一些 Bean 跟 SpringBootApplication 不在一个路径下面,或者多个不同路径下面,这个就起到作用了。

以上面的示例来说,其实@AutoConfigurationPackage就相当于在启动类加上@ComponentScan(basePackages = "sample.tomcat")。

三、@Import(AutoConfigurationImportSelector.class)

1)、AutoConfigurationImportSelector介绍

@EnableAutoConfiguration注解通过@Import(AutoConfigurationImportSelector.class)向容器中导入了AutoConfigurationImportSelector组件,它实现了DeferredImportSelector,并间接实现了ImportSelector接口,重写了selectImports()方法,帮助我们返回所有需要被注册为bean的类全限定类名的数组集合。

public class AutoConfigurationImportSelector
    implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
    BeanFactoryAware, EnvironmentAware, Ordered {
        //...
    }

我们先来看下AutoConfigurationImportSelector的类图:

AutoConfigurationImportSelector实现的是DeferredImportSelector接口,而不是ImportSelector接口,两者的执行时间是不一样的:

  • 1、ImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理之前,所谓的其他逻辑,包括对@ImportResource、@Bean这些注解的处理;
  • 2、DeferredImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理完毕之后,所谓的其他逻辑,包括对@ImportResource、@Bean这些注解的处理;

2)、自动配置流程

前面介绍到,AutoConfigurationImportSelector实现的是DeferredImportSelector接口,那么下面我们来看看它在哪里发挥作用的。

回到启动类的main()方法: 

SpringApplication.run(SampleTomcatApplication.class, args);

执行run()方法:

// 运行 Spring 应用程序,创建并刷新一个新的ApplicationContext
public ConfigurableApplicationContext run(String... args) {
    // 创建一个任务执行观察器,用于统计run启动过程花了多少时间
    StopWatch stopWatch = new StopWatch();
    // 记录开始时间
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    // exceptionReporters集合用来存储异常报告器,用来报告SpringBoot启动过程的异常
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置了一个名为java.awt.headless的系统属性, 其实是想设置该应用程序,即使没有检测到显示器,也允许其启动. 对于服务器来说,是不需要显示器的,所以要这样设置.
    configureHeadlessProperty();
    // 从spring.factories配置文件中加载到EventPublishingRunListener对象并赋值给SpringApplicationRunListeners
    // EventPublishingRunListener对象主要用来发布SpringBoot启动过程中内置的一些生命周期事件,标志每个不同启动阶段
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 发布启动事件
    listeners.starting();
    try {
        // 创建ApplicationArguments对象,封装了args参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 准备环境,包括系统变量、环境变量、命令行参数、默认变量等
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 配置需要忽略的BeanInfo信息
        configureIgnoreBeanInfo(environment);
        // 启动时控制台打印Banner
        Banner printedBanner = printBanner(environment);
        // 根据不同类型创建不同类型的spring容器ApplicationContext应用程序上下文
        context = createApplicationContext();
        // 加载spring.factories配置文件配置的异常报告器
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 准备上下文,刷新容器前的一些操作
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 刷新应用上下文。完成Spring IOC容器的初始化
        refreshContext(context);
        // 在刷新上下文后调用的钩子,这个方法是一个模板方法
        afterRefresh(context, applicationArguments);
        // 停止记录执行时间
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        // 事件广播,启动完成了
        listeners.started(context);
        // 执行ApplicationRunner、CommandLineRunner的run方法,实现spring容器启动成功后需要执行的一些逻辑
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

上述代码是springboot应用启动的核心流程,我们重点关注refreshContext(context),也就是刷新Spring的IOC容器方法,自动配置就是在里面实现的。

refreshContext()方法实际上是调用org.springframework.context.support.AbstractApplicationContext#refresh,这个方法是IOC容器刷新的核心方法,主要包括12个步骤:

  • a、prepareRefresh():容器刷新前的一些预处理工作;
  • b、obtainFreshBeanFactory():创建DefaultListableBeanFactory工厂,给bean工厂设置一些属性,加载配置文件信息,封装成bean定义信息;
  • c、prepareBeanFactory(beanFactory):设置bean工厂的一些属性,如添加一些BeanPostProcessor增强器等
  • d、postProcessBeanFactory(beanFactory):模板方法,留给子类扩展实现;
  • e、invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor的postProcessBeanFactory()增强方法;
  • f、registerBeanPostProcessors(beanFactory):注册BeanPostProcessor增强器,注意这里只是注册,真正是在初始化阶段的前后执行;
  • g、initMessageSource():初始化MessageSource,国际化处理;
  • h、initApplicationEventMulticaster():初始化事件多播器;
  • i、onRefresh():模板方法,留给子类扩展实现;
  • j、registerListeners():注册一些监听器;
  • k、finishBeanFactoryInitialization(beanFactory):完成非懒加载的单例bean对象的实例化,包括反射创建bean对象、属性填充、循环依赖的处理、bean的初始化等等;
  • l、finishRefresh():容器刷新完成之后的一些处理工作;

重点关注上述的第五步:invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor的postProcessBeanFactory()增强方法;这一步会执行invokeBeanDefinitionRegistryPostProcessors()方法,然后执行BeanDefinitionRegistryPostProcessor后置处理器的postProcessBeanDefinitionRegistry()增强方法。

其中就包含了ConfigurationClassPostProcessor的处理,加载bean定义信息、ConfigurationClassParser解析自动配置相关的类。

调用栈如下:

org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:232)
	org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275)
		org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95)
			org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:691)
				org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:528)

BeanDefinitionRegistryPostProcessor主要完成对@Configuration注解的处理,类图如下:

我们查看ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法的源码: 

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

在processConfigBeanDefinitions(registry)中通过ConfigurationClassParser#parse()方法解析bean定义信息:

查看ConfigurationClassParser#parse()方法:

parse()方法主要包括两个步骤:

2-1)、ConfigurationClassParser#parse()

主要是调用ConfigurationClassParser#processConfigurationClass来完成解析配置类:

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

真正解析配置类是在ConfigurationClassParser#doProcessConfigurationClass()方法,这里涉及解析很多自动配置相关的注解,如@PropertySource、@ComponentScan、@Import、@ImportResource等。 

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        processMemberClasses(configClass, sourceClass);
    }

    // Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // Process any @ComponentScan annotations
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        for (AnnotationAttributes componentScan : componentScans) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // Check the set of scanned definitions for any further config classes and parse recursively if needed
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // Process any @ImportResource annotations
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // Process individual @Bean methods
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);

    // Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // No superclass -> processing is complete
    return null;
}

自动配置注解的@Import就是在这里解析的。查看ConfigurationClassParser#processImports(),看下具体是如何解析@Import注解的:

查看源码可知,如果@Import导入的是DeferredImportSelector类型,则会将selector先保存到deferredImportSelectorHandler中,并不会马上执行ImportSelector的selectImports()方法,等到所有的注解都解析完成后,再执行;而非DeferredImportSelector类型,则直接调用ImportSelector的selectImports()方法,然后注入对应的Bean对象;

org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)方法解析完注解bean定义信息后,才会执行延迟ImportSelector的bean导入,即调用this.deferredImportSelectorHandler.process()

这里也印证了AutoConfigurationImportSelector实现的DeferredImportSelector的执行时机,是在@Configguration注解中的其他逻辑被处理完毕之后,包括对@ImportResource、@Bean这些注解的处理。

2-2)、this.deferredImportSelectorHandler.process()

回到org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)方法的最后一行: 

this.deferredImportSelectorHandler.process();
public void process() {
    // 获取到上一步暂存在deferredImportSelectors中的ImportSelector
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            // 创建延迟ImportSelector的分组处理器,内部是分组的
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            // 排序
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            // 第一步:register()
            deferredImports.forEach(handler::register);
            // 第二步:processGroupImports()
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

下面详细分析两个重要步骤。

2-2-1)、第一步:register()

register()方法主要作用是注册分组,DeferredImportSelectorGroupingHandler根据Group来分组,用于区分不同的ImportSelector。

  • 1、groupings: key:组类型(在这里 AutoConfigurationGroup) value:组;
  • 2、configurationClasses:key:配置类的注解属性 value:配置类信息;

2-2-2)、第二步:processGroupImports()

看下grouping.getImports()。ConfigurationClassParser.DeferredImportSelectorGrouping#getImports(): 

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

getImports()处理包括两步:

2-2-2-1)、process()方法

AutoConfigurationImportSelector内部定义了AutoConfigurationGroup,它实现了DeferredImportSelector.Group接口,并重写了process()和selectImports()方法。

所以,这里就会执行的是重写后的AutoConfigurationImportSelector.AutoConfigurationGroup#process()方法: 

// 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类
@Override
public void process(AnnotationMetadata annotationMetadata,
        DeferredImportSelector deferredImportSelector) {
    Assert.state(
            deferredImportSelector instanceof AutoConfigurationImportSelector,
            () -> String.format("Only %s implementations are supported, got %s",
                    AutoConfigurationImportSelector.class.getSimpleName(),
                    deferredImportSelector.getClass().getName()));
    // 自动装配的方法入口

    // 调用getAutoConfigurationEntry()方法得到自动配置类并封装成autoConfigurationEntry对象
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(getAutoConfigurationMetadata(),
                    annotationMetadata);
    // 将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    // 遍历所有自动配置类
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        // 将符合条件的自动配置类存入到map中:Map<String, AnnotationMetadata> entries
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

AutoConfigurationImportSelector#getAutoConfigurationEntry()方法,获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费。

// 获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
    // 再次判断是否开启自动配置功能,判断是否有配置spring.boot.enableautoconfiguration属性,默认为true。
    if (!isEnabled(annotationMetadata)) {
        // 未开启自动配置,直接返回{}
        return EMPTY_ENTRY;
    }
    // 获取@EnableAutoConfiguration注解的 exclude 和 excludeName 属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 获取META-INF/spring.factories文件配置的所有自动配置类,实际上是通过SpringFactoriesLoader.loadFactoryNames()获取
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 删除重复的自动配置类,实际上就是将List包装成LinkedHashSet去重
    configurations = removeDuplicates(configurations);

    // 获取到需要排除自动装配的类、类名称,以及配置文件中(属性名:spring.autoconfigure.exclude)配置的类
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);

    // 检查configurations中哪些是需要排除的
    checkExcludedClasses(configurations, exclusions);

    // 真正从候选自动配置类集合中移除掉需要排除的那些配置类
    configurations.removeAll(exclusions);

    // 执行AutoConfigurationImportFilter过滤器match方法判断是否符合@ConditionalOnBean,@ConditionalOnClass或@ConditionalOnWebApplication,循环所有的候选的自动配置组件,判断有没有哪些组件不符合条件的,将它们排除掉
    configurations = filter(configurations, autoConfigurationMetadata);

    // 获取了符合条件的自动配置类后,触发AutoConfigurationImportEvent事件,目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 最终的返回的结果,封装成AutoConfigurationEntry
    return new AutoConfigurationEntry(configurations, exclusions);
}

AutoConfigurationImportSelector#getCandidateConfigurations 获取候选的自动配置类。 

实际上是通过类加载器加载"META-INF/spring.factories"文件中EnableAutoConfiguration对应的自动配置类全类名。

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

自动配置的类全类名拿到后,执行selectImports()。

2-2-2-2)、selectImports()方法 

获取所有要排除的自动配置类,然后删除不符合条件的那些自动配置类,最后做了一下排序。

至此,getImports()方法执行结束,然后遍历所有获取到的自动配置类的全限定类名,挨个调用processImports()方法注入bean。

我们回到org.springframework.context.annotation.ConfigurationClassParser#processImports()方法:

调用addImportBeanDefinitionRegistrar()方法将bean定义注册到importBeanDefinitionRegistrars中,最后通过ImportBeanDefinitionRegistrar#registerBeanDefinitions()方法注册对应的bean。

我们看org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions的如下代码,parse()方法前面已经分析完成了,获取到了自动配置的bean的定义信息,接下来会执行:this.reader.loadBeanDefinitions(configClasses);加载bean定义信息,注册bean到spring容器中。

到这里,SpringBoot的自动配置原理就分析完了。

猜你喜欢

转载自blog.csdn.net/Weixiaohuai/article/details/128854079