Spring Boot 启动过程可以分为三个主要步骤:准备阶段、应用上下文创建阶段和刷新阶段。Spring Boot 启动过程的源码主要位于 spring-boot-autoconfigure、spring-boot-starter、spring-boot 等模块中。下面是一个简要的启动过程源码分析:
本次Spring Boot是基于2.1.0 分析。Spring boot 每个版本有些变化,读者尽量和我保持一致,以防源码有些出入。
一.启动过程
- 准备阶段
在准备阶段,Spring Boot 会读取应用程序的配置信息并加载到 Spring 环境中。这部分的源码主要位于 spring-boot-autoconfigure 模块中的 AutoConfigurationImportSelector 类中。在这个类中,首先会加载默认的配置文件,如 application.properties,然后会读取用户自定义的配置文件,如 application.yml。这些配置信息会被封装成一个 Environment 对象,并且会注册到 Spring 环境中。同时,这个类还会扫描所有的 Spring Boot 自动配置类,将这些自动配置类注册到 Spring 环境中。
- 应用上下文创建阶段
在应用上下文创建阶段,Spring Boot 会根据读取到的配置信息创建应用程序上下文。这部分的源码主要位于 spring-boot 模块中的 SpringApplication 类中。在这个类中,首先会创建一个 ApplicationContextInitializer 对象,并将其注册到应用程序上下文中。然后,这个类会根据读取到的配置信息创建一个 ApplicationContext 对象,并将其设置为主应用程序上下文。接着,这个类会检测是否有 Web 应用程序上下文,并根据需要创建出相应的 Web 应用程序上下文。这个过程中,Spring Boot 会扫描所有的 Bean 定义,并根据这些 Bean 定义创建出相应的 Bean 实例。
- 刷新阶段
在刷新阶段,Spring Boot 会将应用程序上下文中的所有 Bean 进行初始化和依赖注入。同时,Spring Boot 还会执行各种初始化操作,如加载数据库驱动、创建数据库连接池等。这部分的源码主要位于 spring-context、spring-core 等模块中。在初始化完成后,Spring Boot 会启动内嵌的 Tomcat 服务器或其他 Web 服务器,将 Web 应用程序发布到服务器上。当应用程序启动完成后,Spring Boot 会将控制权交给应用程序,等待处理请求。
二.源码分析
创建SpringApplication
启动类入口SpringApplication



1.设置应用类型
这个过程非常重要,直接决定了项目的类型,应用类型分为三种,都在WebApplicationType这个枚举类中,如下:
- NONE:顾名思义,什么都没有,正常流程走,不额外的启动web容器,比如Tomcat。
- SERVLET:基于servlet的web程序,需要启动内嵌的servletweb容器,比如Tomcat。
- REACTIVE:基于reactive的web程序,需要启动内嵌reactiveweb容器
2.设置初始化器(Initializer)

初始化器ApplicationContextInitializer 第一步获取初始化器的名称,详细源码在loadFactoryNames()方法中了,跟着源码进入,最终调用的是#SpringFactoriesLoader.loadSpringFactories()方法。loadSpringFactories()方法就不再详细解释了,其实就是从类路径META-INF/spring.factories中加载ApplicationContextInitializer的值。在spring-boot-autoconfigure的spring.factories文件中的值如下图:


3.设置监听器(Listener)
监听器(ApplicationListener)主要用于监听特定的事件(ApplicationEvent),比如IOC容器刷新、容器关闭等。Spring Boot扩展了ApplicationEvent构建了SpringApplicationEvent这个抽象类,主要用于Spring Boot启动过程中触发的事件,比如程序启动中、程序启动完成等。

从源码中知道其实和初始化器(ApplicationContextInitializer)执行的是同一个方法,同样是从META-INF/spring.factories文件中获取。在spring-boot-autoconfigure的spring.factories

总结:
SpringApplication的构建都是为了run()方法启动做铺垫,最重要的部分就是设置应用类型、设置初始化器、设置监听器。( 「注意」 :初始化器和这里的监听器都要放置在 spring.factories 文件中才能在这一步骤加载,否则不会生效,因此此时 IOC容器 还未创建,即使将其注入到 IOC容器 中也是不会生效的)。

执行run()方法

获取、启动运行过程监听器
//从spring.factories中获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
getRunListeners()方法,其实还是调用了loadFactoryNames()方法从spring.factories文件中获取值,如下:
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
最终注入的是EventPublishingRunListener这个实现类,创建实例过程肯定是通过反射了,因此我们看看它的构造方法


//执行starting()方法
listeners.starting(bootstrapContext, this.mainApplicationClass);
执行SpringApplicationRunListeners的starting()方法,执行了multicastEvent()方法,广播了ApplicationStartingEvent事件。遍历ApplicationListener的实现类,找到监听ApplicationStartingEvent这个事件的监听器,执行onApplicationEvent()方法。
2.环境构建
加载了系统环境配置、用户自定义配置; 广播ApplicationEnvironmentPreparedEvent事件,触发监听器。
//要用于加载系统配置以及用户的自定义配置(application.properties)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

3.创建IOC容器
context = createApplicationContext();
=================================
/**
* Strategy method used to create the {@link ApplicationContext}. By default this
* method will respect any explicitly set application context or application context
* class before falling back to a suitable default.
* @return the application context (not yet refreshed)
* @see #setApplicationContextClass(Class)
*/
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
//webApplicationType决定创建的类型
//这里的是servlet,因此创建的是AnnotationConfigServletWebServerApplicationContext
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
4.IOC容器的前置处理
prepareContext(context, environment, listeners, applicationArguments,printedBanner);

调用初始化器
/**
* Apply any {@link ApplicationContextInitializer}s to the context before it is
* refreshed.
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
//在SpringApplication构建过程中设置的初始化器,从spring.factories取值的遍历执行
//将自定义的ApplicationContextInitializer放在META-INF/spring.factories中,在此时也是会被调用
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
加载启动类,注入容器
// Load the sources
//在SpringApplication构建过程中将主启动类放置在primarySources这个集合中,
// 此时的getAllSources()即是从其中取值
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//取出主启动类,当然你的项目中可能不止一个,将其加载到IOC容器中
//将主启动类加载到beanDefinitionMap,后续该启动类将作为开启自动配置化的入口
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
两次广播事件
两次事件广播,分别是ApplicationContextInitializedEvent和ApplicationPreparedEvent
listeners.contextPrepared(context);
5.刷新容器
刷新容器,比如初始化资源,初始化上下文广播器等。
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
//调用创建的容器applicationContext中的refresh()方法
((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
* 刷新上下文环境
*/
prepareRefresh();
/**
* 初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等
* 添加ApplicationContextAwareProcessor处理器
* 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
* 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
*/
prepareBeanFactory(beanFactory);
try {
/**
* 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
*/
postProcessBeanFactory(beanFactory);
/**
* 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
* 执行对应的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
/**
* 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
* 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
*/
registerBeanPostProcessors(beanFactory);
/**
* 初始化上下文中的资源文件,如国际化文件的处理等
*/
initMessageSource();
/**
* 初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher
*/
initApplicationEventMulticaster();
/**
* 给子类扩展初始化其他Bean
*/
onRefresh();
/**
* 在所有bean中查找listener bean,然后注册到广播器中
*/
registerListeners();
/**
* 设置转换器
* 注册一个默认的属性值解析器
* 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
* 初始化剩余的非惰性的bean,即初始化非延迟加载的bean
*/
finishBeanFactoryInitialization(beanFactory);
/**
* 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理
* 即对那种在spring启动后需要处理的一些类,这些类实现了ApplicationListener<ContextRefreshedEvent>,
* 这里就是要触发这些类的执行(执行onApplicationEvent方法)
* 另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
* 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
*/
finishRefresh();
}
finally {
resetCommonCaches();
}
}
}
6.IOC容器的后置处理
afterRefresh(context, applicationArguments);
7.发出结束执行的事件
listeners.started(context);
同样是EventPublishingRunListener这个监听器,广播ApplicationStartedEvent事件。但是这里广播事件和前几次不同,并不是广播给SpringApplication中的监听器(在构建过程中从spring.factories文件获取的监听器)。因此在IOC容器中注入的监听器(使用@Component等方式注入的)也能够生效。前面几个事件只有在spring.factories文件中设置的监听器才会生效。这里并没有用事件广播器SimpleApplicationEventMulticaster广播事件,而是使用ConfigurableApplicationContext直接在IOC容器中发布事件。
8.执行Runners
callRunners(context, applicationArguments);
callRunners(context, applicationArguments),它用于调用 Runner。Runner 是 Spring Boot 中的一个重要概念,它是一个用于执行特定任务的组件。在应用程序启动时,Spring Boot 会自动扫描并调用所有实现了 CommandLineRunner 和 ApplicationRunner 接口的 Bean,执行它们的 run 方法。这些 Bean 可以用来初始化数据、创建测试数据等操作。