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,然后完成启动。
posted @ 2019-04-23 16:34  redreampt  阅读(414)  评论(0编辑  收藏  举报