SpringBoot

SpringBoot是什么

SpringBoot 是一个轻量级、快速开发框架。整合了常用的第三方依赖整合(原理:通过Maven子父工程的方式);简化 XML 配置,全部采用注解形式;内置 Http 服务器(Jetty和Tomcat),最终以java 应用程序(Main函数)进行执行。

SpringBoot核心特征

  • Springboot 项目为独立运行的 spring 项目,java -jar xx.jar 即可运行
  • 内嵌 Servlet 容器(可以选择内嵌: tomcat,jetty等服务器)
  • 提供了 starter 的 pom 配置简化 maven 的配置
  • 自动配置 Spring 容器中的 bean。当不满足实际开发场景,可自定义 bean 的自动化配置
  • 准生产的应用监控(基于:ssh, http, telnet对服务器运行的项目进行监控)
  • Springboot 无需做出 xml 配置,也不是通过代码生成来实现(通过条件注解)

SpringBoot相比于Spring框架有什么优势

SpringMVC 是基于 Servlet 的一个 MVC 框架,主要解决 WEB 开发的问题,因为 Spring 的配置非常复杂,各种 xml,properties 处理起来比较繁琐。于是为了简化开发者的使用,Spring 社区创造性地推出了 SpringBoot,它遵循约定优于配置,极大降低了 Spring 使用门槛,但又不失 Spring 原本灵活强大的功能。SpringBoot 具有如下优点:简化编码;简化配置;简化部署;简化监控

常见SpringBoot注解

@Autowired

Spring2.5引入了@Autowired注释,它可以对类成员变量、方法(setter)及构造函数进行标注,完成自动装配的工作。Spring通过一个BeanPostProcessor对@Autowired进行解析,所以要让@Autowired起作用必须事先在Spring容器中声明AutowiredAnnotationBeanPostProcessor这个Bean。例如:

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

当Spring容器启动时,AutowiredAnnotationBeanPostProcessor将扫描Spring容器中所有Bean,当发现Bean中拥有@Autowired注释时就找到和其匹配(默认按类型匹配)的Bean,并注入到对应的地方。当@Autowired作用于setter方法或构造方法上时,@Autowired将与查找方法参数中引用类型匹配的Bean自动注入。值得注意的的是,@Autowired自动注入时,如果找不到匹配的Bean或有多个匹配的Bean时将会抛出异常,解决方法是使用@Autowired(required = false)与使用@Qualifier注释指定注入Bean的名称从而消除歧义,例如:

@Autowired
public void setOffice(@Qualifier("office")Office office) {
    this.office = office;
}

@Qualifier(“office”)中的office是Bean的名称,所以@Autowired和@Qualifier结合使用时,自动注入的策略就从byType转变成byName。

@Resource

@Resource与@Autowired作用类似,区别在于@Autowired按照byType自动注入,而@Resource默认按照byName自动注入。Spring将@Resource注释的name属性解析为Bean的名字,而type属性则解析为Bean的类型。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。要使@Resource生效还需在Spring容器中注册一个负责处理这些注释的BeanPostProcessor:

package com.baobaotao;
import javax.annotation.Resource;
public class Boss {
    @Resource
    private Car car;    // 自动注入类型为 Car 的 Bean
    @Resource(name = "office")
    private Office office;  // 自动注入 bean 名称为 office 的 Bean
}
@Inject

@Inject等价于默认的@Autowired,只是没有required属性。

@Component

使用@Component可以完全消除配置文件中对Bean的定义,@Component标注于类上并配合@Autowired即可。需要注意的是,要在配置文件中额外配置context:component-scan/用于扫描组件

@Scope

用于标注类从而指定单例还是原型

@SpringBootApplication

@SpringBootApplication注解是SpringBoot的核心注解,实际上由诸多注解组成的组合注解.其中主要的三个注解分别为:@SpringBootConfiguration;@EnableAutoConfiguration;@ComponentScan。

@SpringBootConfiguration

@SpringBootConfiguration继承于@Configuration,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到Spring容器中,并且实例名就是方法名。

@EnableAutoConfiguration

@EnableAutoConfiguration注解就是SpringBoot能自动进行配置的魔法所在。主要是通过此注解,能把所有符合自动配置条件的bean的定义加载到Spring容器中。

@ComponentScan

@ComponentScan注解默认情况下会扫描@SpringBootApplication所在包及其子包下被@Component,@Controller,@Service,@Repository等注解标记的类并纳入到spring容器中进行管理。具体功能如下:

  • 自定扫描路径下边带有@Controller,@Service,@Repository,@Component注解加入Spring容器
  • 通过includeFilters加入扫描路径下没有以上注解的类加入spring容器
  • 通过excludeFilters过滤出不用加入spring容器的类
@Controller

在SpringMVC中,控制器Controller负责处理由DispatcherServlet分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model,然后再把该Model返回给对应的View进行展示。使用@Controller标记一个类是Controller,然后使用@RequestMapping和@RequestParam等一些注解用以定义URL请求和Controller方法之间的映射,这样的Controller就能被外界访问。此外Controller不会直接依赖于HttpServletRequest和HttpServletResponse等HttpServlet对象,它们可以通过Controller的方法参数灵活的获取。

@RestController

@RestController的编写方式依赖注解组合,@RestController被@Controller和@ResponseBody标注,表示@RestController具有两者的注解语义,因此在注解处理时@RestController比@Controller多具有一个@ResponseBody语义,这就是@RestController和@Controller的区别。

@RequestMapping

@RequestMapping用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。一个实例如下:

@RequestMapping(value = "/helloworld", method = {RequestMethod.GET, RequestMethod.POST},
    consumes = "application/json", produces = "application/json", params = "myparam=myvalue",
    headers = "Refer=")
public String helloworld(Model model) {
    model.addAttribute("message", "Hello World");
    return "welcome";
}
  • value指定请求路径
  • method指定该方法仅处理某些HTTP请求,可同时支持多个HTTP请求
  • consumes指定方法处理请求的提交内容类型
  • produces指定返回的内容类型,该类型bi必须是request请求头(Accept)中所包含的类型
  • params指定request中必须包含某些参数值时,才让该方法处理
  • headers指定request中必须包含指定的header值,才让该方法处理
@RequestBody

@RequestBody注解允许request的参数在reqeust体中,常常结合前端POST请求,进行前后端交互。

@ResponseBody

@ResponseBody是作用在方法上的,@ResponseBody表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用【也就是AJAX】。在使用@RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上 @ResponseBody后,会直接返回json数据。

@RequestParam

@RequestParam 用来接收URL中的参数,如/param?username=001,可接收001作为参数,例如:

@RequestMapping("/users")
@ResponseBody
public String param(@RequestParam String username){
    return "user" + username;
}
@RequestHeader

@RequestHeader用于将请求的头信息区数据映射到功能处理方法的参数上。

@PostConstruct和@PreDestroy

Spring允许在Bean在初始化完成后以及Bean销毁前执行特定的操作,既可以通过实现InitializingBean/DisposableBean接口来定制初始化之后/销毁之前的操作方法,也可以通过元素的init-method/destroy-method属性指定初始化之后/销毁之前调用的操作方法。@PostConstruct和@PreDestroy则对应于上述过程。然而不同于上述方式,使用@PostConstruct和@PreDestroy注释却可以指定多个初始化/销毁方法,那些被标注@PostConstruct或@PreDestroy注释的方法都会在初始化/销毁时被执行。

@Configuration

@Configuration中所有带@Bean注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。从定义来看,@Configuration注解本质上还是@Component,因此context:component-scan/或者@ComponentScan都能处理@Configuration注解的类。

@Service

@Service表示被标注的类是业务层Bean。@Service(“userService”)注解是告诉Spring,当Spring要创建UserServiceImpl的的实例时,bean的名字必须叫做"userService"。

@Repository

@Repository对应数据访问层Bean,@Repository(value=“userDao”)注解是告诉Spring,让Spring创建一个名字叫“userDao”的UserDaoImpl实例。

@CookieValue

@CookieValue用于将请求的Cookie数据映射到功能处理方法的参数上。

Spring Boot的核心

入口类和@SpringBootApplication

Spring Boot的项目一般都会有*Application的入口类,入口类中会有main方法,这是一个标准的Java应用程序的入口方法。

@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:

@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 {

该注解主要组合了以下注解:

①. @SpringBootConfiguration:这是Spring Boot项目的配置注解,这也是一个组合注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

在Spring Boot项目中推荐使用@ SpringBootConfiguration替代@Configuration

②. @EnableAutoConfiguration:启用自动配置,该注解会使Spring Boot根据项目中依赖的jar包自动配置项目的配置项:

a) 如:我们添加了spring-boot-starter-web的依赖,项目中也就会引入SpringMVC的依赖,Spring Boot就会自动配置tomcat和SpringMVC

java osgi与springboot整合 springboot整合了什么_spring

③. @ComponentScan:默认扫描@SpringBootApplication所在类的同级目录以及它的子目录。

全局配置文件

Spring Boot项目使用一个全局的配置文件application.properties或者是application.yml,在resources目录下或者类路径下的/config下,一般我们放到resources下。

①. 修改tomcat的端口为8088

java osgi与springboot整合 springboot整合了什么_spring_02

重新启动应用,查看效果:

java osgi与springboot整合 springboot整合了什么_java_03

②. 修改进入DispatcherServlet的规则为:*.html

java osgi与springboot整合 springboot整合了什么_初始化_04

测试:

java osgi与springboot整合 springboot整合了什么_MVC_05

java osgi与springboot整合 springboot整合了什么_java_06

Spring Boot自动化配置原理

典型的Spring Boot应用的启动类一般均位于src/main/java根路径下

比如MoonApplication类:

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

其中@SpringBootApplication开启组件扫描和自动配置,而SpringApplication.run则负责启动引导应用程序。

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

@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 {
     // ......
 }

@SpringBootConfiguration就是@Configuration,它是Spring框架的注解,标明该类是一个JavaConfig配置类。

而@ComponentScan启用组件扫描,这里着重关注@EnableAutoConfiguration。@EnableAutoConfiguration注解表示开启Spring Boot自动配置功能,Spring Boot会根据应用的依赖、自定义的bean、classpath下有没有某个类 等等因素来猜测你需要的bean,

然后注册到IOC容器中。

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

首先看下它的定义:

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

关注点应该在@Import(EnableAutoConfigurationImportSelector.class)上了,前文说过,@Import注解用于导入类,并将这个类作为一个bean的定义注册到容器中,这里将把EnableAutoConfigurationImportSel000000000000000000000000ector作为bean注入到容器中,而这个类会将所有符合条件的@Configuration配置都加载到容器中,看看它的代码:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
     // 省略了大部分代码,保留一句核心代码
     // 注意:SpringBoot最近版本中,这句代码被封装在一个单独的方法中
     // SpringFactoriesLoader相关知识请参考前文
     List<String> factories = new ArrayList<String>(new LinkedHashSet<String>(
           SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader)));
 }

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

// 来自 org.springframework.boot.autoconfigure下的META-INF/spring.factories
// 配置的key = EnableAutoConfiguration,与代码中一致
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration\
.....

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

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
 }

分别说一说:

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

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

// 提供对datasource配置信息的支持,所有的配置前缀为:spring.datasource
 @ConfigurationProperties(prefix = spring.datasource)
 public class DataSourceProperties {
     private ClassLoader classLoader;
     private Environment environment;
     private String name = testdb;
     ......
 }

@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class }):导入其他额外的配置,就以DataSourcePoolMetadataProvidersConfiguration为例吧。

@Configuration
public class DataSourcePoolMetadataProvidersConfiguration {

     @Configuration
     @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
     static class TomcatDataSourcePoolMetadataProviderConfiguration {
         @Bean
         public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
             .....
         }
     }
   ......
 }

DataSourcePoolMetadataProvidersConfiguration是数据库连接池提供者的一个配置类,即Classpath中存在org.apache.tomcat.jdbc.pool.DataSource.class,则使用tomcat-jdbc连接池,如果Classpath中存在HikariDataSource.class则使用Hikari连接池。

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

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

整个流程很清晰,但漏了一个大问题:

EnableAutoConfigurationImportSelector.selectImports()是何时执行的?其实这个方法会在容器启动过程中执行:AbstractApplicationContext.refresh(),更多的细节在下一小节中说明。

启动引导

SpringApplication初始化

SpringBoot整个启动流程分为两个步骤:初始化一个SpringApplication对象、执行该对象的run方法。看下SpringApplication的初始化流程,SpringApplication的构造方法中调用initialize(Object[] sources)方法,其代码如下:

private void initialize(Object[] sources) {
      if (sources != null && sources.length > 0) {
          this.sources.addAll(Arrays.asList(sources));
      }
      // 判断是否是Web项目
      this.webEnvironment = deduceWebEnvironment();
      setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
      setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
      // 找到入口类
      this.mainApplicationClass = deduceMainApplicationClass();
 }

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

ApplicationContextInitializer的主要目的是在ConfigurableApplicationContext做refresh之前,对ConfigurableApplicationContext实例做进一步的设置或处理。

ConfigurableApplicationContext继承自ApplicationContext,其主要提供了对ApplicationContext进行设置的能力。

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

而ApplicationListener的目的就没什么好说的了,它是Spring框架对Java事件监听机制的一种框架实现,具体内容在前文Spring事件监听机制这个小节有详细讲解。这里主要说说,如果你想为Spring Boot应用添加监听器,该如何实现?

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

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

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

org.springframework.context.ApplicationListener=\ cn.moondev.listeners.xxxxListener\

关于SpringApplication的初始化,我们就说这么多。

Spring Boot启动流程

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

public ConfigurableApplicationContext run(String... args) {
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
         ConfigurableApplicationContext context = null;
         FailureAnalyzers analyzers = null;
         configureHeadlessProperty();
         // ①
         SpringApplicationRunListeners listeners = getRunListeners(args);
         listeners.starting();
         try {
             // ②
             ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
             ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
             // ③
             Banner printedBanner = printBanner(environment);
             // ④
             context = createApplicationContext();
             // ⑤
             analyzers = new FailureAnalyzers(context);
             // ⑥
             prepareContext(context, environment, listeners, applicationArguments,printedBanner);
             // ⑦
              refreshContext(context);
             // ⑧
             afterRefresh(context, applicationArguments);
             // ⑨
             listeners.finished(context, null);
             stopWatch.stop();
             return context;
        }
         catch (Throwable ex) {
             handleRunFailure(context, listeners, analyzers, ex);
             throw new IllegalStateException(ex);
         }
     }

① 通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners通过调用starting()方法通知所有的SpringApplicationRunListeners:应用开始启动了。

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

还记得初始化流程中,SpringApplication加载了一系列ApplicationListener吗?这个启动流程中没有发现有发布事件的代码,其实都已经在SpringApplicationRunListeners这儿实现了。

简单的分析一下其实现流程,首先看下SpringApplicationRunListener的源码:

public interface SpringApplicationRunListener {
     // 运行run方法时立即调用此方法,可以用户非常早期的初始化工作
     void starting();
     // Environment准备好后,并且ApplicationContext创建之前调用
     void environmentPrepared(ConfigurableEnvironment environment);
     // ApplicationContext创建好后立即调用
     void contextPrepared(ConfigurableApplicationContext context);

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

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

SpringApplicationRunListener只有一个实现类:EventPublishingRunListener。

①处的代码只会获取到一个EventPublishingRunListener的实例

我们来看看starting()方法的内容:

public void starting() {
     // 发布一个ApplicationStartedEvent
     this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
 }

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

listeners.environmentPrepared(environment);

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

② 创建并配置当前应用将要使用的Environment,Environment用于描述应用程序当前的运行环境,其抽象了两个方面的内容:

配置文件(profile)和属性(properties),开发经验丰富的同学对这两个东西一定不会陌生:不同的环境(eg:生产环境、预发布环境)可以使用不同的配置文件,而属性则可以从配置文件、环境变量、命令行参数等来源获取。

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

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

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

配置Environment:配置profile以及properties

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

③、SpringBoot应用在启动时会输出这样的东西:

java osgi与springboot整合 springboot整合了什么_MVC_07

如果想把这个东西改成自己的涂鸦,你可以研究以下Banner的实现

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

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

⑥、初始化ApplicationContext,主要完成以下工作:

将准备好的Environment设置给ApplicationContext

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

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

将所有的bean加载到容器中

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

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

从名字上理解为刷新容器,那何为刷新?就是插手容器的启动,联系一下第一小节的内容。

获取到所有的BeanFactoryPostProcessor来对容器做一些额外的操作。

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

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

ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor

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

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

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注解的时候,就会调用这一小节中的EnableAutoConfigurationImportSelector.selectImports()来完成自动配置功能。

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

⑨、执行所有SpringApplicationRunListener的finished()方法。这就是Spring Boot的整个启动流程,其核心就是在Spring容器初始化并启动的基础上加入各种扩展点,这些扩展点包括:ApplicationContextInitializer、ApplicationListener以及各种BeanFactoryPostProcessor等等。