# Spring Boot启动流程

开发一个 Spring Boot 项目,都会用到如下启动类

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

从中可以看出,关键部分有两个,一个是第 1 行的注解 @SpringBootApplication,一个是第 5 行的 SpringApplication.run(...)。 所以要了解 Spring Boot 的启动流程,从这两位入手就可以了。

下面氛围两部分:

  1. 第一部分,从 @SpringBootApplication 入手,从注解的角度看看 SpringBoot 的关键注解。

注意,在 Java 中,注解只是起到标注的作用,可以理解为一种特殊的 comment,为 类/方法 进行特定标注,本身并不执行代码。真正使注解起作用的,是第二部分的启动过程,利用反射,读取类的注解并进行相应操作。

  1. 第二部分,从 SpringApplication.run() 入手,了解真实的启动过程。

# @SpringBootApplication

@SpringBootApplication 是 SpringBoot 应用的核心注解,它其实是一个组合注解:

@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) })
public @interface SpringBootApplication {
    ...
}

虽然使用了多个 Annotation 进行原信息标注,但是其中重要的只有三个:

  • @Configuration(点开@SpringBootConfiguration会发现里面还是用了@Configuration
  • @EnableAutoConfiguration
  • @ComponentScan

下面分别介绍这三个注解。

# @Configuration

这里的 @Configuration 对我们来说应该不陌生,他就是 JavaConfig 形式的 Spring IoC 容器的配置类使用的那个 @Configuration

所以,这里的启动类标注了 @Configuration 后,本身其实也是一个 IoC 容器的配置类。

# @ComponentScan

这个注解在 Spring 中非常重要,它对应 XML 配置中的元素。@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件(比如 @Component@Repository 等)或者 Bean 定义,最终将这些 Bean 定义加载到 IoC 容器中。

我们可以通过 basPackages 等属性来细粒度定制 @ComponentScan 自动扫描的范围。如果不指定,则 Spring 会默认从声明 @ComponentScan 所在类的 package 进行扫描。

所以 Spring Boot 的启动类最好是放在 root package 下,防止自动扫描范围不对。

# @EnableAutoConfiguration

这个注解同样非常重要,它主要是借助 @Import 的支持,将所有符合自动配置条件的 bean 定义加载到 IoC 容器中。

@EnableAutoConfiguration 作为一个复合注解,其自身定义关键信息如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

其中最关键的就是 @Import(AutoConfigurationImportSelector.class),借助 @AutoConfigurationImportSelector,可以帮助 Spring Boot 应用将所有符合条件的 @Configuration 配置都加载到当前 IoC 容器中。 就像一只“八爪鱼”一样,借助 Spring 框架原有的一个工具类:SpringFactoriesLoader 的支持,@EnableAutoConfiguration 可以智能的自动配置功效才大功告成。

EnableAutoConfiguration

上面提到的 SpringFactoriesLoader 在 Spring 中用到的地方非常多,其主要功能就是从指定的配置文件 META-INF/spring.factories 中加载配置。

public abstract class SpringFactoriesLoader {
    //...
    public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
        ...
    }


    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        ....
    }
}

配合 @EnableAutoConfiguration 使用时,它提供了一种配置查找的功能支持,即根据 @EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的 key,获取对应的一组 @Configuration 类。

SpringFactoriesLoader

上图是从 Spring Boot 的 autoconfigure 依赖包中的 META-INF/spring.factories 中摘录的一段内容,可以很好的说明问题。

所以,@EnableAutoConfiguration 自动配置的魔法其实是:

  • 从 classpath 中搜寻所有的 META-INF/spring.factories 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射(Java Refletion)实例化为对应的标注了 @Configuration 的 JavaConfig 形式的 IoC 容器配置类,然后汇总并加载到 IoC 容器.

相关代码可以从 AutoConfigurationImportSelector.getCandidateConfigurations() 中看出端倪:

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 这里就是利用 SpringFactoiresLoader 从 spring.factories 中读取 @EnableAutoConfiguration 的类
		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;
	}

具体细节在第二部分介绍。

# 启动流程

SpringBoot 应用的 main 函数一般是

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

点进 run 函数,可以发现关键代码为:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

从中可以看出,SpringBoot 的启动过程从大体上可以分为两步:

  • 创建 SpringApplication,即 new SpringApplication(primarySources) 的过程。
  • 运行 SpringApplication,即 .run(args) 的过程。 下面分别讲讲两部分

# 创建 SpringApplication

创建 SpringApplication 的过程主要代码为:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // primarySources 一般为应用的主类,在这里就是上面的 SpringBootExampleApplication 类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // deduceFromClasspath() 主要使用 Class.isPresent() 来判断当前应用的类型(Reactive 还是 Servlet)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // getSpringFactoriesInstances(Class<T> type) 函数主要是利用 SpringFactoriesLoader 去 META-INF/spring.factories 中寻找 key 为 type 的类,并对其实例化
    // 实例化 key 为 org.springframework.boot.BootstrapRegistryInitializer 的类
    this.bootstrapRegistryInitializers = new ArrayList<>(
            getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 实例化 key 为 org.springframework.context.ApplicationContextInitializer 的类
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 实例化 key 为 org.springframework.context.ApplicationListener 的类
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 利用方法调用栈,寻找方法名为 main 的方法,并将其对应的类作为主类
    this.mainApplicationClass = deduceMainApplicationClass();
}

简单来说,这一步做的是将一些关键信息保存在 SpringApplication 的属性中,为后面做准备。

# run(运行过程)

运行过程的主要代码在 SpringApplication 类中的 run 函数中

public ConfigurableApplicationContext run(String ...args) {
    // 记录应用的启动时间
    long startTime = System.nanoTime();
    // 创建应用的引导上下文
    // 主要做的事情是获取到之前加载的所有 bootstrappers,然后挨个执行其 initialize 方法来完成对引导启动器上下文的环境设置
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    // 从 spring.factories 中获取所有的 SpringApplicationRunListeners
    // 默认情况下,SpringBoot 里这里会有唯一一个 SpringApplicationRunListener,即 EventPublishingRunListener
    // EventPublishingRunListener 在初始化的时候,会将 SpringApplication 中的 属性 listeners(即 ApplicationListener)赋值
    // 然后在调用相关方法时,使用 Multicaster 广播给所有的 ApplicationListener。
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 遍历所有 listeners,调用其 starting 方法。
    // 广播 ApplicationStartingEvent。相当于通知所有的 ApplicationListener,应用正在 starting。
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        // 构建应用程序参数持有类
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 创建并配置好当前 SpringBoot 应用将要使用的环境;
        // 广播 ApplicationEnvironmentPreparedEvent 事件,相当于通知所有的 ApplicationListener,应用环境准备好了
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 控制台上打印 banner
        Banner printedBanner = printBanner(environment);
        // 创建 SpringBoot 应用上下文。
        // 默认情况下,这里创建的是 AnnotationConfigServletWebServerApplicationContext。
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        // 准备应用上下文,里面会依次调用之前创建的 ApplicationContextInitializer,对 context 进行初始化操作;
        // 之后还会广播 ApplicationContextInitializedEvent 给所有的 listeners。
        // 然后将 sources(默认为 primarySources,即应用的主类) 加载进上下文
        // 最后广播 ApplicationPreparedEvent 事件,告诉 listeners,应用上下文已经准备好了。
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // refresh 是非常关键的一步,里面会做很多事情,例如 BeanFactory 的设置,BeanFactoryPostProcessor 的执行,BeanPostProcessor 接口的执行,
        // 自动化配置类的解析、条件注解的解析、国际化的初始化等等。具体在下面再单独讲解
        refreshContext(context);
        // refresh 之后应该做的事情,目前是个空实现
        afterRefresh(context, applicationArguments);
        // 计算启动花的时间,并在日志中打印
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }
        // 广播 ApplicationStartedEvent 事件,通知所有 ApplicationListener,应用已经启动了
        listeners.started(context, timeTakenToStartup);
        // 调用上下文中的 ApplicationRunner 和 CommandRunner 接口的实现类
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        // 计算ready花的时间,并广播 ApplicationReadyEvent 事件至所有监听者 listeners
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

这样 run 方法执行完毕后,Spring 容器初始化工作完毕,各种监听器、初始化器也做了相应工作。

下面具体看一下 refreshContext(context) 这一步。

还是以 web 程序为例,这里对应的上下文为 AnnotationConfigServletWebServerApplicationContext。它的 refresh 方法调用了父类 AbstractApplicationContext 的 refresh 方法。

下面首先给出 refresh 的主要代码,然后解释其中的主要方法。

@Override
public void refresh() throws BeansException, IllegalStateException {
    // refresh 过程只能一个线程处理,不允许并发执行
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        // 刷新前的准备,包括 设置flag、时间、初始化 properties 等。
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

# 1. prepareRefresh

  1. 设置容器的启动时间,撤销关闭状态,开启活跃状态。
  2. 初始化属性源信息(Property)。
  3. 验证环境信息里一些必须存在的属性。

# 2. prepareBeanFactory

  1. 设置 beanFactory 的类加载器为当前上下文的类加载器,添加属性编辑注册器(PropertyEditorRegistrar
  2. 添加 ApplicationContextAwareProcessor 这个 BeanPostProcessor,并取消相关 7 个接口的自动注入,因为 ApplicationContextAwareProcessor 把这些接口的实现工作做了。ApplicationContextAwareProcessor 的作用是让 bean 在实现了 EnvironmentAware 等 7 个接口后,可以感知到应用上下文的相关属性。
  3. 设置特殊类型对应的bean。BeanFactory 对应刚刚获取到的 beanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext 这三个接口对应的 bean 都设置为当前上下文。
  4. 注册 ApplicationListenerDetector 这个 BeanPostProcessor。
  5. 注入一些其他信息的 bean,例如 environment、systemProperties 等。

# 3. postProcessBeanFactory

准备好 beanFactory 后的一些后续操作,不同的上下文会执行不同的操作。

  • GenericWebApplicationContext 会在 beanFactory 中添加 ServletContextAwareProcessor,该处理器用于处理实现了 ServletContextAware 接口的 bean。这种类型的 bean 在初始化时,可以感知到 servletContext 和 servletConfig。
  • 另外,AnnotationConfigEmbeddedWebApplicationContext 对应的 postProcessBeanFactory方法为:
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 调用父类,即 GenericWebApplicationContext 对应的 postProcessBeanFactory 函数
    super.postProcessBeanFactory(beanFactory);
    // 查看 basePackages 属性。如果设置了该属性,会使用 ClassPathBeanDefinitionScanner 去扫描 basePackages 包下的 bean 并注册
    if (!ObjectUtils.isEmpty(this.basePackages)) {
        this.scanner.scan(this.basePackages);
    }
    // 查看 annotatedClasses 属性,如果设置了会使用 AnnotatedBeanDefinitionReader 去注册这些 bean
    if (!this.annotatedClasses.isEmpty()) {
        this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
    }
}

# 4. invokeBeanFactoryPostProcessor

在 Spring 上下文中找出实现了 BeanFactoryPostProcessor 接口的 processors 并执行。 Spring 上下文会委托给 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors 方法执行。

BeanFactoryPostProcessor 的作用是用来修改 Spring 上下文中已经存在的 bean 的定义,使用 ConfigurableListableBeanFactory 对 bean 进行处理。

这里的处理逻辑如下。

首先从 Spring 上下文中找出类型为 BeanDefinitionRegistryPostProcessor 的 bean(这些 processor 是在容器刚创建时通过构造 AnnotatedBeanDefinitionReader 的时候注册到容器中的),然后按照优先级分别执行。优先级的逻辑如下:

  1. 先把实现了 PriorityOrdered 接口的 BeanDefinitionRegistryPostProcessor 全部找出来,排序后依次执行其 postProcessBeanDefinitionRegistry 方法。
  2. 把实现了 Ordered 接口的 BeanDefinitionRegistryPostProcessor 全部找出来,排序后依次执行。
  3. 剩下的 BeanDefinitionRegistryPostProcessor 全部找出来并依次执行。
  4. 然后执行以上所有 processor 的 postProcessBeanFactory 接口(因为 BeanDefinitionRegistryPostProcessor 接口是 BeanFactoryPostProcessor 的子类,所以一定可以找到 postProcessBeanFactory 接口)。

接下来从 Spring 上下文中找出类型为 BeanFactoryPostProcessor 的 bean(会忽略掉上面的 bean),然后执行。 这里的查找规则与上面的 BeanDefinitionRegistryPostProcessor 相同,先找 PriorityOrdered,再找 Ordered,最后是两者都没有的。

这里需要重点说明的是 ConfigurationClassPostProcessor 这个 processor,因为这个 processor 是处理 @Configuration 注解的,在 SpringBoot 中非常重要。

ConfigurationClassPostProcessor 是优先级最高的 processor(因为其实现了 PriorityOrdered 接口)。

这个 processor 会去 BeanFactory 中找出所有带有 @Configuration 注解的 bean,然后使用 ConfigurationClassParser 去解析这个类。

ConfigurationClassParser 内部有个 Map<ConfigurationClass, ConfigurationClass> 类型的 configurationClasses,用于保存已经解析的 Configuration 类。 ConfigurationClass 是一个对要解析的配置类的封装,内部存储了配置类的注解信息、被 @Bean 注解修饰的方法、@ImportResource 注解修饰的信息、ImportBeanDefinitionRegistrar 等。

这里 ConfigurationClassPostProcessor 最先被处理还有另外一个原因是如果程序中有自定义的 BeanFactoryPostProcessor,那么这个 PostProcessor 首先得通过 ConfigurationClassPostProcessor 被解析出来,然后才能被 Spring 容器找到并执行。(ConfigurationClassPostProcessor 不先执行的话,这个 Processor 是不会被解析的,不会被解析的话也就不会执行了)。

在简单的程序中,只有主类 SpringBootExampleApplication 有 @Configuration 注解(@SpringBootApplication 中包含此注解),所以这个配置类会被 ConfigurationClassParser 解析。 解析过程如下:

  1. 处理 @PropertySource 注解,进行配置信息的解析。
  2. 处理 @ComponentScan 注解,使用 ComponentScanAnnotationParser 扫描 basePackage 下需要解析的类 (@SpringBootApplication 注解也包括了 @ComponentScan 注解,只不过 basePackages 是空的,空的话会去获取当前 @Configuration 修饰的类所在的包),并注册到 BeanFactory 中(这个时候bean并没有进行实例化,而只是进行了注册。具体的实例化在 finishBeanFactoryInitialization 方法中执行)。对于扫描出来的类,递归解析。
  3. 处理 @Import 注解,先递归找出所有注解,然后过滤出只有 @Import 注解的类,得到 @Import 注解的值。找出所有的 @Import 注解后,开始处理逻辑
    1. 遍历这些 @Import 注解内部的属性类集合。
    2. 如果这个类是 ImportSelector 接口的实现类,实例化这个 ImportSelector。如果这个类还是 DeferredImportSelector 接口的实现类,那么加入 ConfigurationClassParser 的 deferredImportSelectors 属性中,让第 6 步处理;否则调用 ImportSelector 的 selectImports 方法得到需要 import 的类,然后对这些类递归做 @Import 注解的处理。
    3. 如果这个类是 ImportBeanDefinitionRegistrar 接口的实现类,设置到配置类的 importBeanDefinitionRegistrars 属性中。
    4. 其他情况,把这个类加入到 configurationClassParser 的 importStack(队列)属性中,然后把这个类当作 @Configuration 修饰的类,递归重头开始解析这个类。
  4. 处理 @ImportResource 注解:获取 @ImportResource 注解的 locations 属性,得到资源文件的地址信息,然后遍历这些资源文件,并把它们添加到配置类的 importedResources 属性中
  5. 处理 @Bean 注解:获取被 @Bean 修饰的方法,然后添加到配置类的 beanMethods 属性中
  6. 处理 DeferredImportSelector:处理第 3 步 @Import 产生的 deferredImportSelector,调用其 Group 类的 process 方法,找到需要 import 的类,然后调用第 3 步相同的逻辑进行处理(注意是所有的配置类处理完之后才会执行这一步)。

比如查找 @SpringBootApplication 注解的 @Import 注解数据的话,首先发现 @SpringBootApplication 不是一个 @Import 注解,然后递归调用修饰了 @SpringBootApplication 的注解,发现有个 @EnableAutoConfiguration 注解,再次递归发现被 @Import(AutoConfigurationImportSelector.class) 修饰,还有 @AutoConfigurationPackage 注解,再次递归该注解,发现被 @Import(AutoConfigurationPackages.Registrar.class) 修饰。所以 @SpringBootApplication 对应的 @Import 注解有两个,分别是 @Import(AutoConfigurationPackages.Registrar.class) 和 @Import(EnableAutoConfigurationImportSelector.class)。找出所有的 @Import 注解后,开始处理逻辑。

这里 @SpringBootApplication 注解被 @EnableAutoConfiguration 修饰,@EnableAutoConfiguration 注解被 @Import(AutoConfigurationImportSelector.class) 修饰。 所以在第 3 步中,会找出这个被 @Import 修饰的类 AutoConfigurationImportSelector

spring-boot-import

这个类刚好实现了 DeferredImportSelector 接口,因此会在第 6 步被执行。 第 6 步 selectImport 得到的类就是自动化配置类。

需要注意的是,AutoConfigurationImportSelector 由于实现了 DeferredImportSelector 接口,并不是直接调用其 selectImport 方法,而是调用其子类 AutoConfigurationGroupprocess 方法,然后在其中调用 getAutoConfigurationEntry 来读取自动配置类的。

因此直接在 AutoConfigurationImportSelector$selectImport 方法里打断点时,会发现执行不到。比较神奇。

在当前 processor 中,ConfigurationClassParser 解析完配置类后,会将解析完的类存放在其 configurationClasses 属性中。 然后,processor 会创建一个 ConfigurationClassBeanDefinitionReader 去解析这些配置类。

下面这段代码是 ConfigurationClassBeanDefinitionReader 解析配置类的代码:

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        // 对每个配置类,调用 loadBeanDefinitionsForConfigurationClass 方法
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
    TrackedConditionEvaluator trackedConditionEvaluator) {
  // 使用条件注解判断是否需要跳过这个配置类
  if (trackedConditionEvaluator.shouldSkip(configClass)) {
    // 跳过配置类的话,在Spring容器中移除bean的注册
    String beanName = configClass.getBeanName();
    if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
      this.registry.removeBeanDefinition(beanName);
    }
    this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());
    return;
  }

  if (configClass.isImported()) {
    // 如果自身是被 @Import 注释所 import 的,注册自己
    registerBeanDefinitionForImportedConfigurationClass(configClass);
  }
  // 注册方法中被 @Bean 注解修饰的bean
  for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    loadBeanDefinitionsForBeanMethod(beanMethod);
  }
  // 注册 @ImportResource 注解注释的资源文件中的 bean
  loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
  // 注册 @Import 注解中的 ImportBeanDefinitionRegistrar 接口的 registerBeanDefinitions
  loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

稍微总结一下,invokeBeanFactoryProcessors 方法总的来说就是从 Spring 上下文中找出 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 接口的实现类,并按照一定顺序执行。

其中 ConfigurationClassPostProcessor 这个 BeanDefinitionRegistryPostProcessor 优先级最高,它会对项目中的 @Configuration 注解修饰的类(还有 @Component,@ComponentScan,@Import,@ImportResource 修饰的类也会被处理)进行解析,解析完成后把这些 bean 注册到 BeanFactory 中。

需要注意的是,这个时候注册进来的 bean 还没有被实例化。

下图是对 ConfigurationClassPostProcessor 的总结: configuration-annotation-process

# 5. registerBeanPostProcessors

这一步的主要目的是从 Spring 上下文中找出实现了 BeanPostProcessor 接口的 bean,并设置到 beanFactory 的 beanPostProcessors 属性中。 之后 bean 被实例化的时候,相关 BeanPostProcessor 会被调用。

该方法委托给了 PostProcessorRegistrationDelegate 类的 registerBeanPostProcessors 方法执行。这里的过程与 invokeBeanFactoryPostProcessors 类似:

  1. 先找出实现了 PriorityOrdered 接口的 BeanPostProcessor,然后排序,然后加载到 BeanFactory 的 BeanPostProcessor 集合中。
  2. 再找出实现了 Ordered 接口的 BeanPostProcessor,然后排序,然后加载到 BeanFactory 的 BeanPostProcessor 集合中。
  3. 再找出剩下的 BeanPostProcessors,排序后加入到 BeanFactory 的 BeanPostProcessor 集合中。

在注册之前,beanFactory 里有 4 个 beanPostProcessors,它们是: before-register-post-processor

注册之后,beanFactory 里有 11 个 beanPostProcessors,它们是: after-register-post-processor

这里的 beanPostProcessors 包括 AutowiredAnnotationBeanPostProcessor(用来处理被 @Autowired 注释修饰的 bean) 等。

# 6. initMessageSource

配置 beanFactory 的 messageSource 属性,可以用来做类似国际化的事情。

# 7. initApplicationEventMulticaster

在 Spring 上下文中初始化事件广播器,事件广播器用于事件的发布。

具体来说,在 Spring 上下文中寻找名称为 applicationEventMulticaster 的 bean:

  • 找到的话,将其设置为 beanFactory 的 applicationEventMulticaster 属性。
  • 如果找不到该 bean 的话,则 new 一个 SimpleApplicationEventMulticaster 并设置为 applicationEventMulticaster

# 8. onRefresh

根据 context 的类型,创建一些特殊的 bean。

例如,web 程序的上下文 AnnotationConfigEmbeddedWebApplicationContext 会调用 createEmbeddedServletContainer 方法去创建内置的 Servlet 容器。

目前 SpringBoot 支持的内置容器有:

  • Tomcat、Jetty、Netty、Undertow

# 9. registerListeners

把 Spring 上下文中的应用监听器(applicationListeners)都注册到事件广播器(applicationEventMulticater)中。

然后在 Spring 上下文的 beanFactory 中寻找所有实现了 ApplicationListener 接口的 bean,将它们也加入到 applicationEventMulticater 中。(注意这里并不对它们做实例化,仅仅把 beanName 加入进去)

# 10. finishBeanFactoryInitialization

实例化 beanFactory 中已经被注册但是还没有实例化的所有 bean。(除了懒加载的 bean)。

例如上面 invokeBeanFactoryPostProcessors 方法根据各种注解解析出来的 bean,这个时候都会被实例化。

实例化的过程中,beanPostProcessor 开始起作用。

# 11. finishRefresh

refresh 结束阶段需要做的事情。

  1. 初始化生命周期处理器,并设置到 Spring 上下文的 lifecycleProcessor 属性中。
  2. 调用 lifecycleProcessoronRefresh 方法,这个方法会找出 Spring 上下文中实现了 SmartLifecycle 接口的类,并进行 start 方法的调用。
  3. 发布 ContextRefreshedEvent 事件告知 applicationListeners,进行相应操作。
  4. 调用 LiveBeansView 的 registerApplicationContext 方法:如果设置了 JMX 相关的属性,则就调用该方法。