回顾一下,上篇文章已经说到,SpringApplication初始化 (new SpringApplication()) 做了四件事:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
- 配置源
- 推断web应用类型
- 应用上下文初始器和应用事件监听
- 推导引用主类
我个人觉得,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
这里先看一下类关系图:
可以看到,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服务的
启动web服务
WebServer
是一个接口,实现他的类如下:
这个接口也很简单,只有以下几个方法:
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;
}
在这里面调用了WebServer
的start()
,启动了Tomcat。
总结
SpringApplication初始化完成之后,调用run方法来初始化启动web服务,过程如下:
- 初始化
AnnotationConfigServletWebServerApplicationContext
- 调用父类
ServletWebServerApplicationContext
的onRefresh
方法初始化web服务 - 调用
finishRefresh()
启动web服务