SpringBoot启动流程

SpringBoot的启动流程大致分为创建SpringApplication运行SpringApplication两大步。

  • ①创建SpringApplication。

    • 启动程序执行SpringApplication.run方法,作用是创建了一个Springpplication实例并执行run方法,这里先分析创建一个Springpplication实例的过程,即调用new SpringApplication()。
      1
      2
      3
      4
      5
      6
      @SpringBootApplication
      public class HelloTestApplication {
      public static void main(String[] args) {
      SpringApplication.run(HelloTestApplication.class, args);
      }
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
        public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
      return run(new Class<?>[] { primarySource }, args);
      }

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

      public SpringApplication(Class<?>... primarySources) {
      this(null, primarySources);
      }

      public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
      //二、构造方法中的逻辑:
      this.resourceLoader = resourceLoader;
      // 1、一定要指定primarySources
      Assert.notNull(primarySources, "PrimarySources must not be null");
      this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
      // 2、deduce(推断)web类型(servlet、reactive、NoWeb)
      this.webApplicationType = WebApplicationType.deduceFromClasspath();
      // 3、从META-INF/spring.factories中获取
      setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
      // 4、从META-INF/spring.factories中获取
      setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
      // 5、推断执行的main方法的定义类
      this.mainApplicationClass = deduceMainApplicationClass();
      }
      • ①调用this.resourceLoader = resourceLoader初始化资源加载器为null。

      • ②断言主要加载资源类不能为null,否则报错。

      • ③初始化主要加载资源类集合primarySources并去重。primarySources即保存了主配置类的信息。

      • ④调用WebApplicationType.deduceFromClasspath()推断当前WEB应用类型。

      • ⑤调用setInitializers()设置应用上下文初始化器。其中重点调用了getSpringFactoriesInstances()方法,最终创建了7个初始化器。

        • ①获取当前线程上下文类加载器。

        • ②获取ApplicationContextInitializer的实例名称集合并去重。即找到类路径下所有META-INF/spring.factories文件解析并获取ApplicationContextInitializer所有配置的类路径名称。

        • ③根据以上类路径创建初始化器实例列表。

        • ④调用AnnotationAwareOrderComparator.sort(instances)初始化器实例列表排序后返回实例对象。

      • ⑥调用setListeners()设置监听器。即找到类路径下所有META-INF/spring.factories文件解析并获取ApplicationListener所有配置的类路径名称。逻辑和上方的创建初始化器ApplicationContextInitializer实例列表一样,最终创建了11个监听器。

      • ⑦推断主入口应用类。通过构造一个运行时异常,再遍历异常栈中的方法名,获取方法名为main的栈帧,从来得到入口类的名字再返回该类。

  • ②运行SpringApplication。即执行SpringApplication的run方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    public ConfigurableApplicationContext run(String... args) {
    // 1 StopWatch开启计时
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 这是设置系统属性java.awt.headless,请百度了解 java.awt.headless的用途。
    configureHeadlessProperty();
    // 2、获取到 META-INF/spring.factories中配置的SpringApplicationRunListener
    // 疑问:又出一个Listener,这个SpringApplicationRunListener是监听什么的?
    // 通过它的接口定义、注释了解它的用途
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 这里就调用它的starting()
    listeners.starting();
    try {
    // 命令行参数包装为了ApplicationArguments
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // 3、准备好了Environment,此刻Environment中都有哪些配置参数了?
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    configureIgnoreBeanInfo(environment);
    // 4、打印springboot LOGo图标
    Banner printedBanner = printBanner(environment);
    // 5、创建ApplicationContext context = createApplicationContext();
    context = createApplicationContext();
    // 6、获取到 META-INF/spring.factories中配置的SpringBootExceptionReporter
    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    new Class[] { ConfigurableApplicationContext.class }, context);
    // 7、准备ApplicationContext
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    // 8、刷新ApplicationContext
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    stopWatch.stop();
    if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    }
    // 9、发布started事件 listeners.started(context);
    listeners.started(context);
    // 10、执行所有的Runners
    callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, listeners);
    throw new IllegalStateException(ex);
    }

    try {
    // 11、发布running中事件
    listeners.running(context);
    }
    catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, null);
    throw new IllegalStateException(ex);
    }
    // 12、返回ok的ConfigurableApplicationContext
    return context;
    }
    • ①创建一个StopWatch并执行start方法,用于记录任务的执行时间。

    • ②配置Headless属性,让当前应用进入Headless模式,即给系统属性设置了java.awt.headless。Headless模式是在缺少显示屏、键盘或者鼠标时候的系统配置。

    • ③获取到所有运行监听器。即在文件META-INF\spring.factories中获取SpringApplicationRunListener接口的实现类EventPublishingRunListener,主要发布SpringApplicationEvent。这里找到了一个运行监听器SpringApplicationRunListener。

    • ④遍历所有的运行监听器并调用其starting()方法。相当于通知所有感兴趣系统正在启动过程的人,项目正在starting。

    • ⑤保存命令行参数args,即把输入参数转成DefaultApplicationArguments类。

    • ⑥创建Environment并设置比如环境信息,系统熟悉,输入参数和profile信息。

      • ①由于这里是原生Servlet编程,所以创建了一个StandardServletEnvironment。

      • ②调用configureEnvironment()配置环境信息。

        • ①给容器中添加类型转换器conversionService,这里有136个。

        • ②调用configurePropertySources()加载外部配置源,读取所有的配置源的配置属性值。

        • ③调用configureProfiles()激活profile环境内容。

      • ③调用上方创建的运行监听器SpringApplicationRunListener的environmentPrepared方法。

    • ⑦打印Banner信息。即平常启动项目后打印的Spring标志图。

    • ⑧根据项目类型(Servlet)创建容器AnnotationConfigServletWebServerApplicationContext。

    • ⑨在文件META-INF\spring.factories中获取SpringBootExceptionReporter接口的实现类FailureAnalyzers。

    • ⑩准备IOC容器的基本信息。

      • ①往IOC容器中保存环境信息。

      • ②调用postProcessApplicationContext()执行IOC容器的后置处理流程,即给容器中加入一些类型转换器等。

      • ③调用applyInitializers()执行应用初始化器,即遍历之前创建的所有ApplicationContextInitializer实例(7个)并执行其initialize()方法,来对ioc容器进行初始化的扩展工作。

      • ④调用listeners.contextPrepared()遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其contextPrepared()方法。

      • ⑤如果延迟加载,在上下文添加处理器LazyInitializationBeanFactoryPostProcessor。

      • ⑥调用listeners.contextLoaded()遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其contextLoaded()方法。

    • ⑪刷新ioc容器。会来到熟悉的Spring容器初始化过程,在之前的文章中有详细分析到:https://perfectcode.top/2020/12/03/Spring%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/

    • ⑫完成容器刷新后的工作,这里是个空方法。

    • ⑬调用listeners.started()遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其started()方法。

    • ⑭调用callRunners()获取容器中的ApplicationRunner和CommandLineRunner组件并按照Order进行排序后遍历所有的runner调用run()方法。

    • ⑮以上如果发生异常,会执行handleRunFailure()来遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其failed()方法。

    • ⑯如果没有发生异常,会执行listeners.running()来遍历之前创建的所有SpringApplicationRunListener实例(1个即EventPublishingRunListener)并执行其running()方法,如果在执行running()过程中有异常则调用handleRunFailure()–>reportFailure()来遍历所有的SpringBootExceptionReporter(1个即FailureAnalyzers)执行其reportException()方法。

    • ⑰至此,ioc容器创建成功并返回。

  • 总结如图:

    • 可以看出SpringBoot启动过程中会在不同阶段运行初始化器、运行监听器以及runner的相关方法(图中粗体文字),所以我们可以自定义初始化器、运行监听器或者runner来实现自定义操作。

      • ①自定义ApplicationContextInitializer。

        1
        2
        3
        4
        5
        6
        public class MyApplicationContextInitializer implements ApplicationContextInitializer {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("MyApplicationContextInitializer ....initialize.... ");
        }
        }
      • ②自定义ApplicationListener。

        1
        2
        3
        4
        5
        6
        public class MyApplicationListener implements ApplicationListener {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("MyApplicationListener.....onApplicationEvent...");
        }
        }
      • ③自定义SpringApplicationRunListener。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        public class MySpringApplicationRunListener implements SpringApplicationRunListener {
        private SpringApplication application;

        public MySpringApplicationRunListener(SpringApplication application, String[] args){
        this.application = application;
        }

        @Override
        public void starting() {
        System.out.println("MySpringApplicationRunListener....starting....");
        }

        @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("MySpringApplicationRunListener....environmentPrepared....");
        }

        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener....contextPrepared....");
        }

        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener....contextLoaded....");
        }

        @Override
        public void started(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener....started....");
        }

        @Override
        public void running(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener....running....");
        }

        @Override
        public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("MySpringApplicationRunListener....failed....");
        }
        }
      • ④自定义ApplicationRunner。

        1
        2
        3
        4
        5
        6
        7
        8
        @Order(1)
        @Component
        public class MyApplicationRunner implements ApplicationRunner {
        @Override
        public void run(ApplicationArguments args) throws Exception {
        System.out.println("MyApplicationRunner...run...");
        }
        }
      • ⑤自定义CommandLineRunner。

        1
        2
        3
        4
        5
        6
        7
        8
        @Order(2)
        @Component
        public class MyCommandLineRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
        System.out.println("MyCommandLineRunner....run....");
        }
        }
      • ⑥添加配置文件spring.factories。

        1
        2
        3
        4
        5
        6
        7
        8
        org.springframework.context.ApplicationContextInitializer=\
        com.example.demo.listener.MyApplicationContextInitializer

        org.springframework.context.ApplicationListener=\
        com.example.demo.listener.MyApplicationListener

        org.springframework.boot.SpringApplicationRunListener=\
        com.example.demo.listener.MySpringApplicationRunListener
      • ⑦运行后控制台打印的主要代码如下。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        MyApplicationListener.....onApplicationEvent...
        MySpringApplicationRunListener....starting....
        MyApplicationListener.....onApplicationEvent...
        MySpringApplicationRunListener....environmentPrepared....
        MyApplicationContextInitializer ....initialize....
        MyApplicationListener.....onApplicationEvent...
        MySpringApplicationRunListener....contextPrepared....
        MyApplicationListener.....onApplicationEvent...
        MySpringApplicationRunListener....contextLoaded....
        MyApplicationListener.....onApplicationEvent...
        MyApplicationListener.....onApplicationEvent...
        MyApplicationListener.....onApplicationEvent...
        MyApplicationListener.....onApplicationEvent...
        MySpringApplicationRunListener....started....
        MyApplicationRunner...run...
        MyCommandLineRunner....run....
        MyApplicationListener.....onApplicationEvent...
        MyApplicationListener.....onApplicationEvent...
        MySpringApplicationRunListener....running....