SpringApplication类用于引导和启动一个Spring应用程序(即SpringBoot开发的应用)。通常用SpringBoot开发一个应用程序时,在主类的main函数中可以通过如下代码启动一个Spring应用:

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

SpringApplication的静态方法run(Class<?> primarySource, String... args))的第一个参数接受一个Spring容器配置类(用Java代码对Spring容器进行配置)。第二个参数是命令行参数。将命令行参数转发给SpringApplication类,就可以在用java命令启动应用时,通过命令行参数对Spring应用做一些配置。
SpringApplication类会做如下事情启动应用:

  • 为应用创建一个合适的ApplicationContext
  • 注册一个CommandLinePropertySource,通过CommandLinePropertySource可以对外暴露命令行参数,并将命令行参数与spring应用中用到的properties关联起来
  • 启动ApplicationContext
  • 执行所有的CommandLineRunner类型bean

下面我们通过SpringApplication的源码详细描述上述这些过程。

构建SpringApplication实例

下面是SpringApplication类静态run方法的源码。可以看到,当我们调用这个静态run方法时,实际上会构造一个SpringApplication实例,然后再调用实例的run方法完成spring应用的启动。

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

下面是SpringApplication的构造函数,它主要完成下面初始化工作:

  • 初始化Spring容器的配置类primarySources
  • 推断应用程序的类型,进而根据应用程序的类型创建恰当的ApplicationContext
  • 初始化指定的ApplicationContextInitializer列表
  • 初始化指定的ApplicationListener列表
  • 推断main class的类名称
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {    this.resourceLoader = resourceLoader;    Assert.notNull(primarySources, "PrimarySources must not be null");    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));    this.webApplicationType = WebApplicationType.deduceFromClasspath();    setInitializers((Collection) getSpringFactoriesInstances(            ApplicationContextInitializer.class));    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));    this.mainApplicationClass = deduceMainApplicationClass();}

下面对这些初始化过程进行一一说明:

  • spring容器配置

SpringApplication能够从各种不同的配置源读取bean的定义。Spring Boot建议采用Java注解配置的方式提供一个全局唯一的配置类。但是,你可以同时使用多种不同的配置源。如果是Java注解的配置方式,会使用AnnotatedBeanDefinitionReader加载配置(通过全类名)。如果是XML的配置方式,则会使用XmlBeanDefinitionReader加载配置(通过XML文件地址)。
如果除了primarySources配置类以外,还需要其它的ApplicationContext配置源,则可以调用SpringApplication#setSources(Set<String> sources)方法进行设置,该方法的参数既可以接受一个配置类的全类名,也可以是一个XML配置文件的地址。

  • 推断应用程序类型

SpringApplication默认的应用类型只有三种:

public enum WebApplicationType {    /**     * 非web类应用,无需内嵌web server     */    NONE,    /**     * servlet类型的web应用,需要启动内嵌的web server     */    SERVLET,    /**     * reactive类型的web应用,需要启动内嵌的reactive web server     * 啥是reactive类型的web应用?目前还不知道^_^     */    REACTIVE;

判断的逻辑也非常简单,就是检查classpath下是否存在对应的类。

  • 如果classpath下存在org.springframework.web.reactive.DispatcherHandler类,则应用类型是REACTIVE
  • 如果classpath下存在org.springframework.web.servlet.DispatcherServlet类,则应用类型是SERVLET
  • 如果上面两个DispatcherServlet类都不存在,则应用类型是NONE

应用类型直接决定了要创建的ApplicationContext类型,下表整理了三种应用类型和所创建的ApplicationContext间的对应关系:

应用类型ApplicationContext类型
NONEAnnotationConfigApplicationContext
SERVLETAnnotationConfigServletWebServerApplicationContext
REACTIVEAnnotationConfigReactiveWebServerApplicationContext
  • 初始化ApplicationContextInitializer&ApplicationListener

初始化ApplicationContextInitializer和ApplicationListener的过程比较相似,都是借助于SpringFactories的方式完成初始化的,所以放到一起讲述。