spring-boot-mvc启动流程
1、前言
在web应用开发中,我们比较常用的是spring boot+spring mvc的组合。该组合的核心点就是内嵌tomcat和mvc的dispatcherServlet,今天,我们就来探究一下其启动流程。
前置知识点:spring boot原理、spring mvc原理
源码版本:spring boot 2.1.7
2、流程
此处先给出结论,抓住主线,便于后面的学习。
核心步骤分四步:
- 容器类型确定
- 创建web容器
- 启动内嵌tomcat
- 启动DispatcherServlet
大致时序图如下:
3、容器类型
容器类型,由WebApplicationType枚举类定义:
-
web容器
- mvc容器
- reactive容器
-
非web容器
SpringApplication的构造方法中设置
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
。。。。
//调用方法设置容器类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
。。。。
this.mainApplicationClass = deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath()获取容器类型
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
//reactive
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
//普通容器
return WebApplicationType.NONE;
}
}
//返回servlet容器,即mvc
return WebApplicationType.SERVLET;
}
- WebApplicationType.REACTIVE - 当类路径中存在REACTIVE_WEB_ENVIRONMENT_CLASS并且不存在MVC_WEB_ENVIRONMENT_CLASS时
- WebApplicationType.NONE - 也就是非Web型应用(Standard型),此时类路径中不包含WEB_ENVIRONMENT_CLASSES中定义的任何一个类时
- WebApplicationType.SERVLET - 类路径中包含了WEB_ENVIRONMENT_CLASSES中定义的所有类型时
4、创建web容器
在上步,我们已经确定了容器的类型,接下来就是创建容器了。核心步骤:
- 根据类型获取定义的Class
- 通过反射创建容器
源码如下:
**SpringApplication.run()方法中的createApplicationContext()**执行
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境,确认activefile,确认配置的文件的内容全部可用,此处会准备bootstarpapplication,
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
. ...
//创建容器
context = createApplicationContext();
...
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器,关键点,tomcat在这里面的onrefresh中创建
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
...
return context;
}
createApplicationContext()方法。根据容器类型创建容器,mvc的容器为ServletWebServerApplicationContext。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
//根据容器type加载class
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
//需要的servletWeb容器的Class,mvc
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) {
...
}
}
//根据class创建并返回web容器
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
5、 启动tomcat
我们观察项目启动的日志,会有tomcat的相关日志。debug找到相关的类,便于确定调用堆栈。
Debug的调用堆栈如下:
![](https://i-blog.csdnimg.cn/blog_migrate/fba13621d1d15c290ea703de51c916ce.png)
我们跟着调用堆栈走,先看看onRefrensh()方法。
5.1、onRefresh
ServletWebServerApplicationContext实现了o