回顾一下,上篇文章已经说到,SpringApplication初始化 (new SpringApplication()) 做了四件事:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
}
  1. 配置源
  2. 推断web应用类型
  3. 应用上下文初始器和应用事件监听
  4. 推导引用主类

我个人觉得,SpringBoot的整体源码比较复杂,看源码时带着问题出发比较好,不然没有一个目标,为了看源码而看源码,容易出现只见森林,不见树木的情况。

准备工作做完,接下来就该进入程序的运行阶段了,对于一个SpringBoot应用,得益于它内置了Tomcat容器,我们可以把web程序打包成jar包,直接启动,看SpringBoot是如何启动Tomcat的。

根据web类型初始化上下文

直接单击run方法的源码:

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//设置Headless模式 系统缺少显示设备、键盘或鼠标这些外设的情况下使用该模式
        configureHeadlessProperty();
        //初始化监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
            //打印banner
			Banner printedBanner = printBanner(environment);
            //创建上下文
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //刷新上下文
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

上面的代码比较多,但是如果我们只分析tomcat的话,关注上下文的创建和刷新就行了,对应的两个方法为:

createApplicationContext();
refreshContext(context);

挨个看这两个方法做了什么:

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				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);
	}

之前说过了,webApplicationType是web的应用类型,这里通过这个应用类型找到相应的实现类,并且通过反射的方式实例化这个类。我们这里是servlet,所以实例化的类为AnnotationConfigServletWebServerApplicationContext

创建上下文说完了,接下来看看刷新上下文做了什么:

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
}
protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
}

这里将AnnotationConfigServletWebServerApplicationContext强制转换为AbstractApplicationContext这里先看一下类关系图:

springboot源码中tomact什么时候启动的_spring boot

可以看到,AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,最终是继承了AbstractApplicationContext,所以这里可以进行强制转换为它的父类,调用它父类的refresh()方法:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 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 {
				//...省略无关代码

				// 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) {
			
			}
			finally {
				resetCommonCaches();
			}
		}
	}

初始化web服务

我们看到这里调用了onRefresh()方法,这个方法的实现最终是交给了它的子类ServletWebServerApplicationContext

@Override
protected void onRefresh() {
	super.onRefresh();
	try {
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}
private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

可以看到,这里调用了createWebServer方法,这个方法里面通过ServletWebServerFactory工厂初始化了WebServer,这个时候只是初始化完成web服务,但是Tomcat还没有真正的启动。

我们点击进ServletWebServerFactory看看它是怎么初始化这个web服务的

springboot源码中tomact什么时候启动的_Server_02

启动web服务

WebServer是一个接口,实现他的类如下:

springboot源码中tomact什么时候启动的_spring boot_03

这个接口也很简单,只有以下几个方法:

public interface WebServer {
	void start() throws WebServerException;
	void stop() throws WebServerException;
	int getPort();
}

可以看到,这个接口作用就是为了启动停止web服务。我们在调用onRefresh()中初始化完成了WebServer之后,紧跟着调用了finishRefresh()方法:

@Override
protected void finishRefresh() {
		super.finishRefresh();
		WebServer webServer = startWebServer();
		if (webServer != null) {
			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
		}
	}
private WebServer startWebServer() {
		WebServer webServer = this.webServer;
		if (webServer != null) {
			webServer.start();
		}
		return webServer;
	}

在这里面调用了WebServerstart(),启动了Tomcat。

总结

SpringApplication初始化完成之后,调用run方法来初始化启动web服务,过程如下:

  1. 初始化AnnotationConfigServletWebServerApplicationContext
  2. 调用父类ServletWebServerApplicationContextonRefresh方法初始化web服务
  3. 调用finishRefresh()启动web服务