启动类
SpringBoot应用启动类SpringApplication,提供了两种启动方式(其实底层是一种),如下:
第一种:提供了静态run方法,传入main函数所在类class作为参数
1 | SpringApplication.run(Main.class); //底层调用 new SpringApplication(primarySources).run(args); |
第二种:提供实例run方法,实例化SpringApplication时传入main函数所在类class作为参数
1 | SpringApplication application = new SpringApplication(App.class); |
先分析SpringApplication的构造方法
1 | public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { |
-
根据classpath下是否包含web相关类推断web环境:reactive、servlet、none
-
根据classpath下jar包资源路径下META-INF/spring.factories文件得到工厂类全路径名称并缓存到Map<ClassLoader, MultiValueMap<String, String>> cache,后续需要从cache里根据key(接口全路径名称)找到实现类全路径名称并实例化实现类
-
从2中cache查找接口org.springframework.context.ApplicationContextInitializer的所有实现类全路径名称并实例化存入initializers列表
-
从2中cache查找接口org.springframework.context.ApplicationListener的所有实现类全路径名称并实例化存入listeners列表
-
从RuntimeException().getStackTrace()推断出main函数所在类并实例化
Run方法
-
从上一步2中cache查找接口org.springframework.boot.SpringApplicationRunListener的所有实现类全路径名称并实例化(只有1个实现类EventPublishingRunListener),然后用实例化的EventPublishingRunListener类List实例化类SpringApplicationRunListeners
⚠️:实例化EventPublishingRunListener类时,会将上一步4中的listeners加入到EventPublishingRunListener的多播列表中来
-
运行SpringApplicationRunListeners.starting方法,遍历执行上一步的org.springframework.boot.SpringApplicationRunListener的所有实现类(只有1个实现类:EventPublishingRunListener)的starting方法
⚠️:EventPublishingRunListener的starting方法执行多播,通知多播列表的所有Listeners事件ApplicationStartingEvent,相应的listener(getListener的时候通过事件类型判断listener是否支持此事件类型)对ApplicationStartingEvent事件作出相应(回调onApplicationEvent方法)
-
根据web环境类型实例化Environment:StandardServletEnvironment,并多播通知相应的Listeners事件ApplicationEnvironmentPreparedEvent
⚠️:ConfigFileApplicationListener响应ApplicationEnvironmentPreparedEvent事件,从上一步2中cache查找接口org.springframework.boot.env.EnvironmentPostProcessor的所有实现类全路径名称并实例化,然后依次调用每个实现类的postProcessEnvironment方法,
1
2实现类SystemEnvironmentPropertySourceEnvironmentPostProcessor负责读取系统环境变量,存入StandardServletEnvironment实例
ConfigFileApplicationListener实例化PropertiesLoader和YamlLoader来读取配置文件参数存入StandardServletEnvironment实例 -
打印Banner
-
根据web环境类型实例化ApplicationContext:class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
注意:实例化AnnotationConfigServletWebServerApplicationContext时,会传入AnnotatedBeanDefinitionReader实例和ClassPathBeanDefinitionScanner实例,用于注解、类路径配置文件描述的bean注入
-
实例化ExceptionReporter类用于异常报告处理
-
prepareContext,向AnnotationConfigServletWebServerApplicationContext实例应用Initializer并设置环境变量Environment实例,然后多播通知ApplicationContextInitializedEvent事件
-
logStartupProfileInfo,打印日志记录启动使用的profile
-
通过AnnotationConfigServletWebServerApplicationContext.beanFactory注册两个单例实例SpringApplicationArguments和SpringBootBanner
-
loadContext,注册启动类bean实例到ApplicationContext,然后添加监听器到ApplicationContext,多播通知ApplicationPreparedEvent事件
-
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) -
afterRefresh,默认实现为空,可以继承自SpringApplication自定义实现
-
多播通知ApplicationStartedEvent事件
-
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 | 线程池对任务异常默认不作处理 |