Spring系列之 SpringBoot启动流程

Spring系列之 SpringBoot启动流程

我叫Spring,今天为大家介绍我体内最核心的运行原理之一——Spring Boot的启动原理,帮助大家在面试和走向架构岗位的路上做出一点点贡献。各位程序员朋友们想要启动我,这个过程本质上是由@SpringBootApplication注解驱动的,它实际上是@ComponentScan@EnableAutoConfiguration@Configuration这三个注解的组合。

@EnableAutoConfiguration是最为核心的部分,有了它之后,在启动时会导入“自动配置AutoConfigurationImportSelector类,这个类会将所有符合条件的@Configuration配置加载进容器。也就是说,这个类被标记为配置类,会被加载到容器中。如果启动类中不需要增加额外的配置内容,也不需要指定扫描路径,那么可以仅使用@EnableAutoConfiguration替代@SpringBootApplication,同样可以完成启动。

run()这个方法开始执行后,会经历如下四个阶段:“容器创建”、“填充容器”,所谓的“服务构建”,这里的“服务”就是指Spring Boot应用本身,这个阶段就是用一大堆零件将我组装出来。在我的构造方法里,首先要把传入的“资源加载器”和“主方法类”记录在内存中。然后,我会逐一判断对应的服务类是否存在,来确定Web服务的类型,默认是SERVLET,即基于Servlet的Web服务,如Tomcat;还有响应式非阻塞服务REACTIVE,如Spring WebFlux;如果没有选择任何服务,则使用NONE。确定了选择哪个Web服务之后,然后就要加载初始化类了。我会去读取所有META-INF/spring.factories文件中的“注册初始化器”、“上下文初始化器”和“监听器”这三类配置。在Spring Boot内部,并没有默认的“注册初始化器”配置,但是有7个“上下文初始化器”和8个“监听器”。这些配置信息会在后续的启动过程中使用到。当然,各位程序员朋友们也可以自定义这三个配置,只需要将其放到工程中的sprin g.factories文件中,我就会将它们一并加载进来。

接下来会通过调用栈(stack trace)判断出main方法所在的类,大概率就是启动类本身,后续过程会使用到。这样,我的Spring服务SpringApplication就构造完成了,然后就是调用它的run()方法进入“环境准备”阶段了。在这个阶段,SpringApplication会准备ConfigurableEnvironment,同时逐一调用刚刚加载的“启动注册初始化器”。不过正如前面提到的,Spring Boot内部并没有默认的BootstrapRegistryInitializer,所以默认情况下并不会执行什么特别的操作。接下来会将java.awt.headless这个设置改为true,表示即使缺少显示器、键盘等输入设备也可以正常启动。然后会启动体内的“运行监听器SpringApplicationRunListeners”,同时发布“启动”事件。这样,各位朋友们就可以通过监听这些事件,在启动流程中加入自定义逻辑。具体的监听器原理我会在后续视频中详细介绍。

接下来就要通过prepareEnvironment()方法“组装启动参数”了。根据不同的Web服务类型会构造不同的环境,默认是Servlet。构造之后会加载很多诸如“系统环境变量”、systemProperties等在内的四组配置信息。这样,后续使用到这些信息就无需重新加载了。启动时传入的环境参数args也会进行设置,例如启动时传入的诸如“开发/生产”环境配置等都会在这一步进行加载。

接下来就会发布“环境准备完成”这个事件,刚加载进来的监听器会监听到这个事件,其中的部分“监听器”会进行相应处理。这包括EnvironmentPostProcessorApplicationListener,它们会去加载spring.factories配置文件中“环境配置后处理器”。这里要注意,监听器通过观察者模式设计,“环境准备完成”是逐个“串行”执行,并不是异步“并行”,需要等待所有监听器都处理完成之后,才会继续走后续的逻辑。

等环境绑定到我体内之后,剩下的就是考虑到刚创建的“可配置环境”在一系列过程中可能已有变化,进而做的补偿。这样,“环境准备”阶段就完成了。

我会将上一阶段准备好的各种养分进行组合,孵化我最最核心的“容器”器官,所谓“容器”——ApplicationContext,也可以称为配置上下文,当然叫做“容器”更好理解。创建过程很简单,默认的服务类型是SERVLET,所以创建的是注解配置的Servlet-Web服务容器。在这个过程中,会用到DefaultListableBeanFactory,用来解析@Component@Bean等,并通过ConfigurationClassPostProcessors把它们都放入容器中。之后,通过prepareContext()方法对容器中的部分属性进行初始化,比如设置“Bean名称生成器”、“资源加载器”、“类型转换器”等。此外,ApplicationInitializer也会被加载,虽然默认没有,但可以自定义。在发布“容器准备完成”监听事件之后,会陆续为容器注册完成“Banner”Bean引用策略和“懒加载策略”等等。最后通过Bean定义加载器将“启动类”在内的资源放入“Bean定义池”BeanDefinitionMap中,以便后续根据Bean定义创建Bean对象。

我体内最核心的“容器”就创建完成啦。在这个过程中,我会创建自身提供的以及各位朋友们自定义的所有Bean对象,这个过程也被大家称为自动装配。这个过程大体分为12个小步骤,不仅包含了我之前介绍过的Bean生命周期管理,还会构造和启动一个Web服务器。这12个步骤我会在下一个视频中为各位朋友们详细介绍。

在“环境准备完成”、“容器准备完成”和“资源加载完成”之后,会回调朋友们自定义实现的Runner接口,至此,启动过程全部完成。

如果喜欢这期视频的话,请一定记得长按点赞按钮。