SpringBoot启动流程

启动类

SpringBoot应用启动类SpringApplication,提供了两种启动方式(其实底层是一种),如下:

第一种:提供了静态run方法,传入main函数所在类class作为参数

1
SpringApplication.run(Main.class); //底层调用 new SpringApplication(primarySources).run(args);

第二种:提供实例run方法,实例化SpringApplication时传入main函数所在类class作为参数

1
2
SpringApplication application = new SpringApplication(App.class);
application.run(args); // 同 application.run();

先分析SpringApplication的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// step 1
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// step 2
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// step 3
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// step 4
this.mainApplicationClass = deduceMainApplicationClass();
}
  1. 根据classpath下是否包含web相关类推断web环境:reactive、servlet、none

  2. 根据classpath下jar包资源路径下META-INF/spring.factories文件得到工厂类全路径名称并缓存到Map<ClassLoader, MultiValueMap<String, String>> cache,后续需要从cache里根据key(接口全路径名称)找到实现类全路径名称并实例化实现类

  3. 从2中cache查找接口org.springframework.context.ApplicationContextInitializer的所有实现类全路径名称并实例化存入initializers列表

  4. 从2中cache查找接口org.springframework.context.ApplicationListener的所有实现类全路径名称并实例化存入listeners列表

  5. 从RuntimeException().getStackTrace()推断出main函数所在类并实例化

Run方法

  1. 从上一步2中cache查找接口org.springframework.boot.SpringApplicationRunListener的所有实现类全路径名称并实例化(只有1个实现类EventPublishingRunListener),然后用实例化的EventPublishingRunListener类List实例化类SpringApplicationRunListeners

    ⚠️:实例化EventPublishingRunListener类时,会将上一步4中的listeners加入到EventPublishingRunListener的多播列表中来

  2. 运行SpringApplicationRunListeners.starting方法,遍历执行上一步的org.springframework.boot.SpringApplicationRunListener的所有实现类(只有1个实现类:EventPublishingRunListener)的starting方法

    ⚠️:EventPublishingRunListener的starting方法执行多播,通知多播列表的所有Listeners事件ApplicationStartingEvent,相应的listener(getListener的时候通过事件类型判断listener是否支持此事件类型)对ApplicationStartingEvent事件作出相应(回调onApplicationEvent方法)

  3. 根据web环境类型实例化Environment:StandardServletEnvironment,并多播通知相应的Listeners事件ApplicationEnvironmentPreparedEvent

    ⚠️:ConfigFileApplicationListener响应ApplicationEnvironmentPreparedEvent事件,从上一步2中cache查找接口org.springframework.boot.env.EnvironmentPostProcessor的所有实现类全路径名称并实例化,然后依次调用每个实现类的postProcessEnvironment方法,

    1
    2
    实现类SystemEnvironmentPropertySourceEnvironmentPostProcessor负责读取系统环境变量,存入StandardServletEnvironment实例
    ConfigFileApplicationListener实例化PropertiesLoader和YamlLoader来读取配置文件参数存入StandardServletEnvironment实例
  4. 打印Banner

  5. 根据web环境类型实例化ApplicationContext:class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext

    注意:实例化AnnotationConfigServletWebServerApplicationContext时,会传入AnnotatedBeanDefinitionReader实例和ClassPathBeanDefinitionScanner实例,用于注解、类路径配置文件描述的bean注入

  6. 实例化ExceptionReporter类用于异常报告处理

  7. prepareContext,向AnnotationConfigServletWebServerApplicationContext实例应用Initializer并设置环境变量Environment实例,然后多播通知ApplicationContextInitializedEvent事件

  8. logStartupProfileInfo,打印日志记录启动使用的profile

  9. 通过AnnotationConfigServletWebServerApplicationContext.beanFactory注册两个单例实例SpringApplicationArguments和SpringBootBanner

  10. loadContext,注册启动类bean实例到ApplicationContext,然后添加监听器到ApplicationContext,多播通知ApplicationPreparedEvent事件

  11. refreshContext,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 执行context refreshing前的准备工作比如一些属性的初始化
    prepareRefresh()
    // 配置BeanFactory的上下文环境特性如类加载器、后置处理器
    prepareBeanFactory()
    // 注册Bean
    postProcessBeanFactory()
    invokeBeanFactoryPostProcessors()
    registerBeanPostProcessors()
    // 初始化事件多播器到ApplicationContext
    initApplicationEventMulticaster()
    // 注册特定环境相关bean(此处初始化web容器)
    OnRefresh()
    // 添加监听器到事件多播器
    registerListeners()
    // 初始化所有剩下的单例bean
    beanfactory.preInstantiateSingletons()
    // 多播通知ContextRefreshedEvent事件
    publishEvent()
    // 在此处启动web容器,web容器启动完成后,多播通知ServletWebServerInitializedEvent事件
    finishRefresh()
    // 向JVM注册优雅停机钩子函数,用于单例释放等操作
    Runtime.getRuntime().addShutdownHook(hook_thread)
  12. afterRefresh,默认实现为空,可以继承自SpringApplication自定义实现

  13. 多播通知ApplicationStartedEvent事件

  14. callRunner,调用ApplicationRunner接口和CommandLineRunner接口的实现类

 
1
CommandLineRunner和ApplicationRunner的区别仅仅是run方法的入参类型不一样,ApplicationRunner中run方法的参数为ApplicationArguments,而CommandLineRunner接口中run方法的参数为String数组
15. 多播通知ApplicationReadyEvent事件

另外SpringBoot启动起来怎么没退出?

JVM退出机制

1. 所有的非daemon线程退出
2. 某个线程显式的调用了System.exit()或Runtime.exit()

所以一定有非daemon线程在运行,当启动tomcat容器时,会启动线程监听端口,因为有这个线程一直在运行,所以JVM不会退出

线程池异常处理

1
线程池对任务异常默认不作处理
-------------本文结束感谢您的阅读-------------
Good for you!