Spring Boot 启动流程分析-基于嵌入式Servlet容器
前言
本文将分析spring boot应用在启动时的几个关键时间点,学习Spring Boot的运行原理。
版本:Spring Boot 1.5.x
Applcation.main()
当我们执行Applcation.main()
方法时,它会创建一个SpringApplication实例,并将该类作为参数传递。
springApplication.initialize()
创建SpringApplication实例时会执行其初始化方法:
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();//检测是否是web环境
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));//添加上下文初始化器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();//添加监听器
}
这些初始化器和监听器大部分与用户没有交际,这里可以先忽略。只有一个监听器需要了解:
该监听器会负责加载Spring Boot的配置文件。
springApplication.run()
然后会调用SpringApplication实例的启动方法,该方法是启动过程的核心方法,需要重点关注:
public ConfigurableApplicationContext run(String... args) {
//...
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);//准备参数,为main函数传递的参数
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);//准备环境参数,包括系统属性、系统环境变量、应用属性等
context = createApplicationContext();//创建应用上下文对象
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);//为应用上下文启动做准备
refreshContext(context);//启动应用上下文
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
//...
}
springApplication.prepareContext()
该方法会做一些准备工作,大部分目前可以忽略,重点是会加载我们的Application
类到应用上下文中。
load(context, sources.toArray(new Object[sources.size()]));
我们的Application
默认添加了@SpringBootApplication
注解,即作为入口配置类,将承担配置的作用。
springApplication.refreshContext()
该方法将启动应用上下文,加载配置类并完成bean的自动扫描、自动装配功能。以及完成嵌入式Servlet容器的创建、启动,ServletContext的配置(如添加Servlet和Filter)等工作。
embeddedWebApplicationContext.refresh()
对于嵌入式web容器的应用上下文来讲,它的主要执行流程是:
1.执行应用上下文的的基本任务,加载配置类并完成bean的创建和装配工作:
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();//主要是创建并装配bean
}
catch (RuntimeException ex) {
stopAndReleaseEmbeddedServletContainer();
throw ex;
}
}
2.创建嵌入式Servlet容器,并添加回调函数:
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());//创建嵌入式Servlet容器并添加启动回调函数
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
3.嵌入式Servlet容器在创建时会自动启动容器,参考TomcatEmbeddedServletContainer.initialize()
,其启动时会调用刚才的回调函数:
this.tomcat.start();//启动tomcat
4.执行回调函数,执行ServletContextInitializer
接口定义的初始化任务。
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareEmbeddedWebApplicationContext(servletContext);//将应用上下文添加到servletContxt中
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);//执行初始化任务,如添加Servlet,Filter
}
}
ServletContextInitializer
是spring boot定义的接口,用于初始化ServletContext,主要用于添加Servlet,Filter实例,在Spring Boot上使用SpringMVC时,dispatcherServlet
就是通过这种方法默认添加的,参考DispatcherServletAutoConfigration
。
5.完成Servlet容器的启动,监听请求并提供服务。
protected void finishRefresh() {
super.finishRefresh();
EmbeddedServletContainer localContainer = startEmbeddedServletContainer();//完成嵌入式Servlet容器的启动
if (localContainer != null) {
publishEvent(
new EmbeddedServletContainerInitializedEvent(this, localContainer));
}
}
总结
本篇大致梳理了以jar包方式,运行Spring MVC时,Spring Boot的启动原理。其主要步骤是:
- 读取配置属性
- 创建应用上下文,读取入口配置类,创建并装配Bean。这个过程中,所有满足条件的start模块会生效。后端模块也是在这个过程中完成初始化,并工作就绪的。
- 创建并开始启动嵌入式Serlvet容器。在嵌入式容器的启动过程中,会触发回调函数,以Java代码的方式配置Web相关功能,如配置Servlet,Filter,然后完成启动。