SpringBoot 启动概述

透过现象看本质,SpringApplication 只是将一个典型的Spring应用的启动流程进行了扩展,因此,透彻理解 Spring 容器是打开 Spring Boot 大门的一把钥匙。

Spring IOC 容器启动概述

BeanDefinition 对象:容器中的每一个 bean 都会有一个对应的 BeanDefinition 实例。该实例负责保存 bean 对象的所有必要信息,包括 bean 对象的 class 类型、是否是抽象类、构造方法和参数、其他属性等等。

BeanDefinitionRegistry 对象: 抽象出 bean 的注册逻辑。registerBeanDefinition、removeBeanDefinition、getBeanDefinition 等注册管理 BeanDefinition 的方法。

BeanFactory 对象: 则抽象出了 bean 的管理逻辑。主要包含 getBean、containBean、getType、getAliases 等管理 bean 的方法。

DefaultListableBeanFactory 对象: 作为一个比较通用的 BeanFactory 实现,它同时也实现了 BeanDefinitionRegistry 接口,因此它就承担了 bean 的注册管理工作。

容器启动阶段

BeanDefinitionReader 对象:它会对加载的 ConfigurationMetaData(配置元数据) 进行解析和分析,并将分析后的信息组装为相应的 BeanDefinition。最后把这些保存了 bean 定义的 BeanDefinition,注册到相应的 BeanDefinitionRegistry。

ps:

这一阶段侧重于 bean 对象管理信息的收集,当然一些验证性或者辅助性的工作也在这一阶段完成。

经过这一阶段,所有 bean 定义都通过 BeanDefinition 的方式注册到 BeanDefinitionRegistry 中。

Bean 的实例化阶段

当某个请求通过容器的 getBean 方法请求某个对象,或者因为依赖关系容器需要隐式的调用 getBean 时,就会触发第二阶段的活动:容器会首先检查所请求的对象之前是否已经实例化完成。

如果没有,则会根据注册的 BeanDefinition 所提供的信息实例化被请求对象,并为其注入依赖。当该对象装配完毕后,容器会立即将其返回给请求方使用。

ps:

BeanFactory 只是 Spring IoC 容器的一种实现,如果没有特殊指定,它采用延迟初始化策略:只有当访问容器中的某个对象时,才对该对象进行初始化和依赖注入操作。

在实际场景下,更多的使用另外一种类型的容器: ApplicationContext,它构建在 BeanFactory 之上,属于更高级的容器。

除了具有 BeanFactory 的所有能力之外,还提供对事件监听机制以及国际化的支持等。它管理的 bean,在容器启动时全部完成初始化和依赖注入操作。

Spring IOC 容器启动扩展机制

IoC 容器负责管理容器中所有 bean 的生命周期,而在 bean 生命周期的不同阶段,Spring 提供了不同的扩展点来改变 bean 的命运。

在容器的启动阶段:

BeanFactoryPostProcessor 允许我们在容器实例化相应对象之前,对注册到容器的 BeanDefinition 所保存的信息做一些额外的操作,比如修改bean定义的某些属性或者增加其他信息等。

如果要自定义扩展类,通常需要实现  org.springframework.beans.factory.config.BeanFactoryPostProcessor 接口。

与此同时,因为容器中可能有多个 BeanFactoryPostProcessor,可能还需要实现 org.springframework.core.Ordered 接口,以保证 BeanFactoryPostProcessor 按照顺序执行。

栗子:

Spring 提供了为数不多的 BeanFactoryPostProcessor 实现,我们以  PropertyPlaceholderConfigurer 来说明其大致的工作流程。

<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="url" value="${jdbc.mysql.url}"/>
</bean>
或者
@Configuration
@ImportResource("classpath:/config/properties.xml")
public class AppConfig {
    @Value("${jdbc.mysql.url}")
    private String url;
}

当 BeanFactory 在第一阶段加载完所有配置信息时,BeanFactory 中保存的对象的属性还是以占位符方式存在的,比如 ${jdbc.mysql.url}。

当 PropertyPlaceholderConfigurer 作为 BeanFactoryPostProcessor 被应用时,它会使用 properties 配置文件中的值来替换相应的 BeanDefinition 中占位符所表示的属性值。

当需要实例化 bean 时,bean 定义中的属性值就已经被替换成我们配置的值。

如果Bean的创建和使用在 PropertySourcesPlaceholderConfigurer 之前,那么就有可能出现占位符没有被处理的情况。

如果在PropertySourcesPlaceholderConfigurer处理之前要用到占位符,以下方案可供参考:

  1. 重写 PropertySourcesPlaceholderConfigurer 使之处理占位符更靠前
  2. 在代码里,显式来处理占位符:environment.resolvePlaceholders("${db.mysql.url}")

在对象实例化阶段:

BeanPostProcessor,它会处理容器内所有符合条件并且已经实例化后的对象。

BeanFactoryPostProcessor 处理 bean 的定义,而 BeanPostProcessor 则处理 bean 完成实例化后的对象。

栗子:

注解、AOP 等功能的实现均大量使用了 BeanPostProcessor。

比如有一个自定义注解,你可以实现 BeanPostProcessor 的接口,在其中判断 bean 对象的脑袋上是否有该注解。如果有,你可以对这个 bean 实例执行任何操作。

在 Spring 中经常能够看到各种各样的 Aware 接口,其作用就是在对象实例化完成以后将 Aware 接口定义中规定的依赖注入到当前实例中。

比如最常见的 ApplicationContextAware 接口,实现了这个接口的类都可以获取到一个 ApplicationContext 对象。

当容器中每个对象的实例化过程走到 BeanPostProcessor 前置处理这一步时,容器会检测到之前注册到容器的 ApplicationContextAwareProcessor。

然后就会调用其 postProcessBeforeInitialization() 方法,检查并设置 Aware 相关依赖。

SpringFactoriesLoader 详解 

类加载器加载类

JVM 提供了 3 种类加载器: BootstrapClassLoader、 ExtClassLoader、 AppClassLoader 分别加载 Java 核心类库、扩展类库以及应用的类路径( CLASSPATH)下的类库。

JVM 通过双亲委派模型进行类的加载,采用双亲委派模型的一个好处是保证使用不同类加载器最终得到的都是同一个对象,这样就可以保证 Java 核心库的类型安全。

比如,加载位于 rt.jar 包中的 java.lang.Object 类,不管是哪个加载器加载这个类,最终都是委托给顶层的 BootstrapClassLoader 来加载的,这样就可以保证任何的类加载器最终得到的都是同样一个 Object 对象。

但双亲委派模型并不能解决所有的类加载器问题,比如,Java 提供了很多服务提供者接口( ServiceProviderInterface,SPI),允许第三方为这些接口提供实现。

常见的 SPI 有 JDBC、JNDI、JAXP 等,这些 SPI 的接口由核心类库提供,却由第三方实现。

这样就存在一个问题:SPI 的接口是 Java 核心库的一部分,是由 BootstrapClassLoader 加载的;SPI 实现的 Java 类一般是由 AppClassLoader 来加载的。

BootstrapClassLoader 是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。

它也不能代理给 AppClassLoader,因为它是最顶层的类加载器。也就是说,双亲委派模型并不能解决这个问题。

线程上下文类加载器( ContextClassLoader)正好解决了这个问题。从名称上看,可能会误解为它是一种新的类加载器,实际上,它仅仅是 Thread 类的一个变量而已。

可以通过 setContextClassLoader(ClassLoader)和 getContextClassLoader()来设置和获取该对象。

如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是 AppClassLoader。

在核心类库使用 SPI 接口时,传递的类加载器使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。

类加载器加载资源

类加载器除了加载 class 外,还有一个非常重要功能,就是加载资源,它可以从 jar 包中读取任何资源文件。

比如, ClassLoader.getResources(Stringname) 方法就是用于读取 jar 包中的资源文件。

它的逻辑其实跟类加载的逻辑是一样的,首先判断父类加载器是否为空,不为空则委托父类加载器执行资源查找任务,直到 BootstrapClassLoader,最后才轮到自己查找。

而不同的类加载器负责扫描不同路径下的 jar 包,就如同加载 class 一样,最后会扫描所有的 jar 包,找到符合条件的资源文件。

类加载器的 findResources(name) 方法会遍历其负责加载的所有 jar 包,找到 jar 包中名称为 name 的资源文件,这里的资源可以是任何文件,甚至是 .class 文件。

SpringFactoriesLoader 的 spring.factories加载

    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                    "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

从 CLASSPATH 下的每个 jar 包中搜寻所有 META-INF/spring.factories 配置文件,然后解析 properties 文件,找到指定名称的配置后返回。

需要注意的是,其实这里不仅仅是会去 ClassPath 路径下查找,会扫描所有路径下的 jar 包,只不过这个文件只会在 ClassPath 下的 jar 包中。

执行 loadFactoryNames(EnableAutoConfiguration.class,classLoader)后,得到对应的一组 @Configuration 类。

我们就可以通过反射实例化这些类然后注入到 IOC 容器中,最后容器里就有了一系列标注了 @Configuration 的 JavaConfig 形式的配置类。

这就是 SpringFactoriesLoader,它本质上属于 Spring 框架私有的一种扩展方案,类似于 SPI,Spring Boot 在 Spring 基础上的很多核心功能都是基于此。

Spring 容器的事件监听机制

事件监听机制多用于图形界面编程,比如:点击按钮、在文本框输入内容等操作被称为事件。当事件触发时,应用程序作出一定的响应。

在服务器端,事件的监听机制更多的用于异步通知以及监控和异常处理。

Java 提供了实现事件监听机制的两个基础类:自定义事件类型扩展自 java.util.EventObject、事件的监听器扩展自 java.util.EventListener。

spring的栗子:

容器中的所有事件类型均继承自  org.springframework.context.AppliationEvent。

容器中的所有监听器都实现 org.springframework.context.ApplicationListener 接口,并且以 bean 的形式注册在容器中。

一旦在容器内发布 ApplicationEvent 及其子类型的事件,注册到容器的 ApplicationListener 就会对这些事件进行处理。

ApplicationEvent 继承自 EventObject,Spring 提供了一些默认的实现,比如:ContextClosedEvent 表示容器在即将关闭时发布的事件类型, ContextRefreshedEvent 表示容器在初始化或者刷新的时候发布的事件类型。

容器内部使用 ApplicationListener 作为事件监听器接口定义,它继承自 EventListener

ApplicationContext 容器在启动时,会自动识别并加载 EventListener 类型的bean,一旦容器内有事件发布,将通知这些注册到容器的 EventListener。

ApplicationContext 接口继承了 ApplicationEventPublisher 接口,该接口提供了 void publishEvent(ApplicationEvent event) 方法定义,ApplicationContext 容器担当的就是事件发布者的角色。

ApplicationContext 将事件的发布以及监听器的管理工作委托给  ApplicationEventMulticaster 接口的实现类。

在容器启动时,检查容器内是否存在名为 applicationEventMulticaster 的 ApplicationEventMulticaster 对象实例。

如果有就使用其提供的实现,没有就默认初始化一个 SimpleApplicationEventMulticaster 作为实现。

如果我们业务需要在容器内部发布事件,只需要为其注入 ApplicationEventPublisher 依赖即可,或者实现 ApplicationEventPublisherAware 接口或者 ApplicationContextAware 接口。

Spring Boot 自动配置原理

@SpringBootApplication 开启组件扫描和自动配置。

SpringApplication.run 则负责启动引导应用程序。

@SpringBootApplication 是一个复合 Annotation,它将三个有用的注解组合在一起:

  • @Configuration,它是 Spring 框架的注解,标明该类是一个 JavaConfig 配置类。
  • @ComponentScan 启用组件扫描。
  • @EnableAutoConfiguration 注解表示开启 Spring Boot 自动配置功能,Spring Boot 会根据应用的依赖、自定义的 bean、Classpath 下有没有某个类等等因素来猜测你需要的 bean,然后注册到 IOC 容器中。

@EnableAutoConfiguration 是如何推算出你的需求?

首先看下它的定义:

@Import 注解用于导入类,并将这个类作为一个 bean 的定义注册到容器中,这里它将把 EnableAutoConfigurationImportSelector 作为 bean 注入到容器中。

这个类会将所有符合条件的 @Configuration 配置都加载到容器中。

这个类会扫描所有的 Jar 包,将所有符合条件的 @Configuration 配置类注入到容器中,何为符合条件,看 META-INF/spring.factories 的文件内容。

栗子:

以 DataSourceAutoConfiguration 为例,看看 Spring Boot 是如何自动配置的:

  • @ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class}):当 Classpath 中存在 DataSource 或者 EmbeddedDatabaseType 类时才启用这个配置,否则这个配置将被忽略。

  • @EnableConfigurationProperties(DataSourceProperties.class):将 DataSource 的默认配置类注入到 IOC 容器中,DataSourceProperties 定义为:

  • @Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class }):导入其他额外的配置

DataSourcePoolMetadataProvidersConfiguration 是数据库连接池提供者的一个配置类。

即 Classpath 中存在 org.apache.tomcat.jdbc.pool.DataSource.class,则使用 tomcat-jdbc 连接池,如果 Classpath 中存在 HikariDataSource.class 则使用 Hikari 连接池。

这里仅描述了 DataSourceAutoConfiguration 的冰山一角,但足以说明 Spring Boot 如何利用条件化配置来实现自动配置的。

总结回顾和发现问题:

  1. @EnableAutoConfiguration 中导入了 EnableAutoConfigurationImportSelector 类。
  2. 这个类的 selectImports() 通过 SpringFactoriesLoader 得到了大量的配置类,而每一个配置类则根据条件化配置来做出决策,以实现自动配置。

整个流程很清晰,但漏了一个大问题: EnableAutoConfigurationImportSelector.selectImports() 是何时执行的?其实这个方法会在容器启动过程中执行: AbstractApplicationContext.refresh()。

Spring Boot 应用启动 SpringBoot启动流程

Spring Boot 整个启动流程分为两个步骤:初始化一个 SpringApplication 对象、执行该对象的 run 方法。

初始化一个 SpringApplication 对象

初始化流程中最重要的就是通过 SpringFactoriesLoader 找到 spring.factories 文件中配置的 ApplicationContextInitializer 和 ApplicationListener 两个接口的实现类名称,以便后期构造相应的实例。

ApplicationContextInitializer 的主要目的是在 ConfigurableApplicationContext 做 refresh 之前,对 ConfigurableApplicationContext 实例做进一步的设置或处理。ConfigurableApplicationContext 继承自 ApplicationContext,其主要提供了对 ApplicationContext 进行设置的能力。

实现一个 ApplicationContextInitializer 非常简单,因为它只有一个方法,但大多数情况下我们没有必要自定义一个 ApplicationContextInitializer。即便是 Spring Boot 框架,它默认也只是注册了两个实现,毕竟 Spring 的容器已经非常成熟和稳定,你没有必要来改变它。

ApplicationListener 是 Spring 框架对 Java 事件监听机制的一种框架实现。

Spring Boot 提供两种方式来添加自定义监听器:

  • 通过 SpringApplication.addListeners(ApplicationListener<?> ...listeners) 或者 SpringApplication.setListeners(Collection<? extends ApplicationListener<?>> listeners)两个方法来添加一个或者多个自定义监听器。

  • 既然 SpringApplication 的初始化流程中已经从 spring.factories 中获取到 ApplicationListener 的实现类,所以也可以直接在自己的 jar 包的 META-INF/spring.factories 文件中新增配置即可:

执行该对象的 run 方法

Spring Boot 应用的整个启动流程都封装在 SpringApplication.run 方法中,其整个流程真的是太长太长了,但本质上就是在 Spring 容器启动的基础上做了大量的扩展。

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
// 1 SpringApplicationRunListeners listeners
= getRunListeners(args); listeners.starting(); try {
// 2 ApplicationArguments applicationArguments
= new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 3 Banner printedBanner
= printBanner(environment);
// 4 context
= createApplicationContext();
// 5 analyzers
= new FailureAnalyzers(context);
// 6 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 7 refreshContext(context);
// 8 afterRefresh(context, applicationArguments);
// 9 listeners.finished(context,
null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }

① 通过 SpringFactoriesLoader 查找并加载所有的 SpringApplicationRunListeners

通过调用 listeners.starting(); 通知所有的 SpringApplicationRunListeners:应用开始启动了。

SpringApplicationRunListeners 其本质上就是一个事件发布者,它在 SpringBoot 应用启动的不同时间点发布不同应用事件类型(ApplicationEvent)。如果有哪些事件监听者(ApplicationListener)对这些事件感兴趣,则可以接收并且处理。

public interface SpringApplicationRunListener {

    /**
     * 运行 run 方法时立即调用此方法,用户可以进行非常早期的初始化工作
     */
    void starting();

    /**
     * EnvironmentPrepared 准备好后,并且 ApplicationContent 创建之前调用*/
    void environmentPrepared(ConfigurableEnvironment environment);

    /**
     * ApplicationContext 创建好后立即调用
     */
    void contextPrepared(ConfigurableApplicationContext context);

    /**
     * ApplicationContent 加载完成,在 refresh 之前调用*/
    void contextLoaded(ConfigurableApplicationContext context);

    /**
     * 当 run 方法结束之前调用*/
    void finished(ConfigurableApplicationContext context, Throwable exception);

}

SpringApplicationRunListener 只有一个实现类: EventPublishingRunListener。

顺着这个逻辑,你可以在 ② 处的 prepareEnvironment() 方法的源码中找到 listeners.environmentPrepared(environment)。

即 SpringApplicationRunListener 接口的第二个方法, environmentPrepared()又发布了另外一个事件 ApplicationEnvironmentPreparedEvent。

② 创建并配置当前应用将要使用的 Environment。

Environment 用于描述应用程序当前的运行环境,其抽象了两个方面的内容:配置文件(profile)和属性(properties)。

不同的环境(eg:生产环境、预发布环境)可以使用不同的配置文件,而属性则可以从配置文件、环境变量、命令行参数等来源获取。

因此,当 Environment 准备好后,在整个应用的任何时候,都可以从 Environment 中获取资源。

总结起来,②处的两句代码,主要完成以下几件事:

  • 判断 Environment 是否存在,不存在就创建(如果是 Web 项目就创建 StandardServletEnvironment,否则创建 StandardEnvironment)。

  • 配置 Environment:配置 profile 以及 properties。

  • 调用 SpringApplicationRunListener 的 environmentPrepared() 方法,通知事件监听者:应用的 Environment 已经准备好。

③ Spring Boot 应用在启动时会输出 Banner

④ 根据是否是 Web 项目,来创建不同的 ApplicationContext 容器。

⑤ 创建一系列 FailureAnalyzer。

创建流程依然是通过 SpringFactoriesLoader 获取到所有实现 FailureAnalyzer 接口的 class,然后再创建对应的实例。FailureAnalyzer 用于分析故障并提供相关诊断信息。

⑥ 初始化 ApplicationContext。

主要完成以下工作:

  • 将准备好的 Environment 设置给 ApplicationContext。

  • 遍历调用所有的 ApplicationContextInitializer 的 initialize() 方法来对已经创建好的 ApplicationContext 进行进一步的处理。

  • 调用 SpringApplicationRunListener 的 contextPrepared() 方法,通知所有的监听者:ApplicationContext 已经准备完毕

  • 将所有的 bean 加载到容器中。

  • 调用 SpringApplicationRunListener 的 contextLoaded() 方法,通知所有的监听者:ApplicationContext 已经装载完毕

⑦ 调用 ApplicationContext 的 refresh() 方法,完成 IOC 容器可用的最后一道工序。

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            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);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

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

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

其中有一项是获取到所有的 BeanFactoryPostProcessor 来对容器做一些额外的操作。BeanFactoryPostProcessor 允许我们在容器实例化相应对象之前,对注册到容器的 BeanDefinition 所保存的信息做一些额外的操作。

这里的 getBeanFactoryPostProcessors() 方法可以获取到 3 个 Processor:

ConfigurationWarningsApplicationContextInitializer 会添加 ConfigurationWarningsPostProcessor

SharedMetadataReaderFactoryContextInitializer 会添加 CachingMetadataReaderFactoryPostProcessor

ConfigFileApplicationListener 会添加 PropertySourceOrderingPostProcessor

有很多很多 BeanFactoryPostProcessor 的实现类,为什么这儿只有这 3 个?

因为在初始化流程获取到的各种 ApplicationContextInitializer 和 ApplicationListener 中,只有上文 3 个做了类似于如下操作:

    protected void addPostProcessors(ConfigurableApplicationContext context) {
        context.addBeanFactoryPostProcessor(
                new PropertySourceOrderingPostProcessor(context));
    }

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(
                new CachingMetadataReaderFactoryPostProcessor());
    }

    @Override
    public void initialize(ConfigurableApplicationContext context) {
        context.addBeanFactoryPostProcessor(
                new ConfigurationWarningsPostProcessor(getChecks()));
    }

进入到 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors() 方法。这个方法除了会遍历上面的 3 个 BeanFactoryPostProcessor 处理外,还会获取类型为 BeanDefinitionRegistryPostProcessor 的 bean: org.springframework.context.annotation.internalConfigurationAnnotationProcessor,对应的 Class 为 ConfigurationClassPostProcessor。 

ConfigurationClassPostProcessor 用于解析处理各种注解,包括:

  • @Configuration

  • @ComponentScan

  • @Import

  • @PropertySource

  • @ImportResource

  • @Bean

当处理 @import 注解的时候,就会走<Spring Boot 自动配置>中的 EnableAutoConfigurationImportSelector.selectImports() 来完成自动配置功能。

⑧ 查找当前 context 中是否注册有 CommandLineRunner 和 ApplicationRunner,如果有则遍历执行它们。

⑨ 执行所有 SpringApplicationRunListener 的 finished() 方法。

这就是 Spring Boot 的整个启动流程,其核心就是在 Spring 容器初始化并启动的基础上加入各种扩展点。

这些扩展点包括:ApplicationContextInitializer、ApplicationListener 以及各种 BeanFactoryPostProcessor 等等。

 

posted @ 2018-10-16 11:08 wade&luffy 阅读(...) 评论(...) 编辑 收藏