springboot启动流程源码分析
之前分析了springboot启动类@SpringBootApplication注解的部分功能。那么接下来分析springboot的启动流程和@SpringBootApplication剩下的功能。
我们来看下springboot启动类有哪些代码
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
MyApplication.class等价于spring的配置类,启动的时候会解析这个类。
args表示运行时指定的参数,就是 java -jar xxx.jar --xxx=xxx
知道了这两点后,来看下run方法做了什么。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 构造SpringApplication对象
return new SpringApplication(primarySources).run(args);
}
其中new SpringApplication(primarySources)
创建SpringApplication对象,run(args)
就是具体的运行逻辑
先来看下new SpringApplication(primarySources)
做了什么
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//把启动类传入的配置类也就是MyApplication 放入到primarySources 属性当中
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 从spring.factories中获取BootstrapRegistryInitializer对象
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 3. 从spring.factories中获取ApplicationContextInitializer对象
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4. 从spring.factories中获取ApplicationListener对象
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 5. 推测出Main类(main()方法所在的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
根据上面源码,构建SpringApplication对象时主要经历一下步骤:
- 推测web应用类型: 主要是通过判断哪些类有,哪些类没有来推断,这里不展开。把推断的结果保存到webApplicationType属性中
- 从spring.factories中获取BootstrapRegistryInitializer对象 通常情况下为空
- 从spring.factories中获取ApplicationContextInitializer对象
- 从spring.factories中获取ApplicationListener对象
- 推测出Main类(main()方法所在的类)
构建完SpringApplication对象后执行run方法启动springboot容器。这才是重点
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
// 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//关于awt的
configureHeadlessProperty();
// 2、从spring.factories中获取SpringApplicationRunListener对象
// 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 3、发布ApplicationStartingEvent
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 4、将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 5、准备Environment
// 包括操作系统,JVM、ServletContext、properties、yaml等等配置
// 会发布一个ApplicationEnvironmentPreparedEvent
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 6、根据应用类型创建Spring容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 7、利用ApplicationContextInitializer初始化Spring容器
// 8、发布ApplicationContextInitializedEvent
// 9、关闭DefaultBootstrapContext
// 10、注册primarySources类,就是run方法存入进来的配置类
// 11、发布ApplicationPreparedEvent事件
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 12、刷新Spring容器,会解析配置类、扫描、启动WebServer
refreshContext(context);
// 空方法
afterRefresh(context, applicationArguments);
// 启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
listeners.started(context, timeTakenToStartup);
// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 15、发布ApplicationFailedEvent事件
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
大致流程是
1. 创建引导启动器,类似一个ApplicationContext
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 利用bootstrapRegistryInitializers初始化DefaultBootstrapContext
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}
bootstrapRegistryInitializers就是之前构建SpringApplication获取到的,一般为空
2. 拿到事件发布对象
SpringApplicationRunListeners listeners = getRunListeners(args);
默认情况下SpringBoot提供了一个EventPublishingRunListener,它实现了SpringApplicationRunListener接口,默认情况下会利用EventPublishingRunListener发布一个ApplicationContextInitializedEvent事件,程序员可以通过定义ApplicationListener来消费这个事件
EventPublishingRunListener有很多方法,每个方法表示一个事件发布的时间节点,如下图
starting : springboot开始启动事件
environmentPrepared:环境变量准备完成事件
contextPrepared:spring容器准备事件
contextLoaded:spring容器准备完成事件
started:spring容器启动事件
ready:spring容器启动成功事件
failed:spring容器启动失败事件
3.发布事件ApplicationStartingEvent
listeners.starting(bootstrapContext, this.mainApplicationClass);
发布springboot开始启动事件,bootstrapContext就是第一步创建的对象, this.mainApplicationClass就是推断出main方法的主类
4、将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
public class DefaultApplicationArguments implements ApplicationArguments {
private final Source source;
private final String[] args;
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
}
5.准备环境变量
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
把系统参数、jvm参数、配置文件的参数封装起来。同时发布环境准备完成事件
这部分和springboot配置优先级有关 下一篇在具体分析
6、根据应用类型创建Spring容器
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
this.webApplicationType 就是开头推断web应用类型的值
应用类型为 SERVLET,则对应AnnotationConfigServletWebServerApplicationContext
应用类型为REACTIVE,则对应AnnotationConfigReactiveWebServerApplicationContext
应用类型为普通类型,则对应AnnotationConfigApplicationContext
7 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
这里面涉及到内容挺多,但是不复杂
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将前面生成的Environment设置到Spring容器中
context.setEnvironment(environment);
// 将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
postProcessApplicationContext(context);
// 利用ApplicationContextInitializer初始化Spring容器
applyInitializers(context);
// 发布ApplicationContextInitializedEvent事件,表示Spring容器初始化完成
listeners.contextPrepared(context);
// Spring容器初始化好了,就关闭DefaultBootstrapContext
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 注册一些单例Bean
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 拿到启动配置类(run方法传进来的类)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将启动配置类解析为BeanDefinition注册到Spring容器中
load(context, sources.toArray(new Object[0]));
// 发布ApplicationPreparedEvent事件,表示Spring容器已经准备好
listeners.contextLoaded(context);
}
这一步会发送两个事件,最终会把spring容器准备完成。注意这个阶段只是spring容器准备好,并没有进行扫描和解析,也就是说此时的spring容器中没有我们定义的bean。
8 刷新Spring容器,会解析配置类、扫描、启动WebServer
spring容器准备完成后就到 refreshContext(context);
这一步就开始解析配置类,也就是解析@SpringBootApplication注解,扫描我们定义bean,解析扫描到的bean,加载到spring容器中。这些步骤就和spring启动流程是一样,spring怎么扫描的,怎么解析的,这里就是一样的。
refreshContext(context);
调用的核心方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
这个refresh()
代码就是spring启动流程中的refresh()
。这部分代码在spring启动过程源码分析中分析过,这里不再赘述。
springboot的refresh()
方法和spring的refresh()
不同的地方在于onRefresh();
方法。spring的onRefresh();
方法是一个空方法,而springboot的onRefresh();
方法涉及到web容器的启动。这部分在springboot整合tomcat的分析中分析过,这里也不再重复。
9 发布spring容器启动事件
listeners.started(context, timeTakenToStartup);
10调用ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
context就是spring容器
applicationArguments 是在第四步中,将run方法的参数封装为applicationArguments
方法流程大概是
- 获取Spring容器中的ApplicationRunner类型的Bean
- 获取Spring容器中的CommandLineRunner类型的Bean
- 执行它们的run()
具体是用来干嘛的呢,下一篇在举个例子分析。
11 发布失败事件
handleRunFailure(context, ex, listeners);
如果springboot启动过程发生异常,发布启动失败的事件
12 发布启动成功事件
listeners.ready(context, timeTakenToReady);
表示springboot启动成功
到此springboot的启动流程就结束了