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)) {//reactivereturn WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {//普通容器return WebApplicationType.NONE;}}//返回servlet容器,即mvcreturn 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加载classif (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET://需要的servletWeb容器的Class,mvccontextClass = 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的调用堆栈如下:

我们跟着调用堆栈走,先看看onRefrensh()方法。

5.1、onRefresh

ServletWebServerApplicationContext实现了onRefresh的方法,该方法在AbstractApplicationContext无实现,交由子类扩展。

实现逻辑如下:

protected void onRefresh() {super.onRefresh();try {//创建webServercreateWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}

5.2、createWebServer

该方法中通过工厂获取webServer,根据不同的类型,servlet容器是tomcat。

private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {//通过工厂获取,先加载工厂,此处是tomcatServletWebServerFactoryServletWebServerFactory factory = getWebServerFactory();//工厂生产产品,tomcatServerthis.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources();
}

5.2、 getWebServerFactory

然后通过容器getBean获取工厂bean,如果还没有,就会创建,进入getBean---->createBean的逻辑。在获取tomcatServletWebServerFactory中,引入了DispatcherServlet

protected ServletWebServerFactory getWebServerFactory() {//名字是tomcatServletWebServerFactoryString[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);....//先获取工厂,然后通过getBean获取工厂bean,此处是tomcatServletWebServerFactoryreturn getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
5.2.1 tomcatServletWebServerFactory注入

上图,获取tomcatServletWebServerFactory工厂,肯定需要该工厂的bean定义,那么定义在哪呢?

是在ServletWebServerFactoryConfiguration.EmbeddedTomcat中注入的,源码如下:

class ServletWebServerFactoryConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedTomcat {//配置TomcatServletWebServerFactory的bean,用于创建tomcatServer@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();......return factory;}}
}
5.2.2、ServletWebServerFactoryConfiguration.EmbeddedTomcat

ServletWebServerFactoryConfiguration.EmbeddedTomcat是由ServletWebServerFactoryAutoConfiguration注入的。

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
//是在auto-configuation中注入的该类
@EnableConfigurationProperties(ServerProperties.class)
//注入了tomcat的工厂
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,//tomcat相关ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new ServletWebServerFactoryCustomizer(serverProperties);}//tomcat的自定义工厂,@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}//注册后置处理器public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}//bean的后置处理器,errorPageRegistrarBeanPostProcessor最后挂到了DispatchServlet上@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class);}}}

ServletWebServerFactoryAutoConfiguration是在auto-configuration的配置文件中注入的。

可以看见图片中,大多servlet相关的自动注入都在此处。

5.2.3、errorPageRegistrarBeanPostProcessor

创建tomcatServletWebServerFactory后对其进行后置处理时,用到了errorPageRegistrarBeanPostProcessor,该后置处理器初始化时最后调用了DispatchServlet

errorPageRegistrarBeanPostProcessor会获取register

private Collection<ErrorPageRegistrar> getRegistrars() {if (this.registrars == null) {// Look up does not include the parent contextthis.registrars = new ArrayList<>(//引入了ErrorPageRegistrar.class,关键点this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);this.registrars = Collections.unmodifiableList(this.registrars);}return this.registrars;}
5.2.4、ErrorPageRegistrar

唯一实现ErrorPageCustomizer,构造方法需要DispatcherServletPath的类

private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {private final ServerProperties properties;//DispatcherServletPathprivate final DispatcherServletPath dispatcherServletPath;//引入DispatcherServletPath,关键点protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {this.properties = properties;this.dispatcherServletPath = dispatcherServletPath;}
}
3.2.5、DispatcherServletPath

DispatcherServletRegistrationBeanDispatcherServletPath的实现,构造方法引入了DispatcherServlet

/*** ServletRegistrationBean for the auto-configured DispatcherServlet. Both registers the servlet and exposes   * DispatcherServletPath information.
*/
public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet>implements DispatcherServletPath {private final String path;//DispatcherServlet,关键类public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {super(servlet);Assert.notNull(path, "Path must not be null");this.path = path;super.addUrlMappings(getServletUrlMapping());}}
5.2.6 DispatcherServlet

DispatcherServlet的由DispatcherServletAutoConfiguration注入如下:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";@Configuration(proxyBeanMethods = false)@Conditional(DefaultDispatcherServletCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })protected static class DispatcherServletConfiguration {//关键//注入了dispatcherServlet@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();......return dispatcherServlet;}}
}

DispatcherServletAutoConfiguration也是由spring.factories的auto-configura注入

5.3、getWebServer

获取webServer,在该方法中,tomcat出场了。

public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}//tomcat出场Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());//连接器Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);//servicetomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);//engineconfigureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);//创建tomcatwebserverreturn getTomcatWebServer(tomcat);
}
5.3.1 getTomcatWebServer

获取TomcatWebServer,原生的tomcat启动有些类似。

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {Assert.notNull(tomcat, "Tomcat Server must not be null");this.tomcat = tomcat;this.autoStart = autoStart;//初始化initialize();
}
//初始化tomcat
private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));synchronized (this.monitor) {try {addInstanceIdToEngineName();Context context = findContext();context.addLifecycleListener((event) -> {if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {// Remove service connectors so that protocol binding doesn't// happen when the service is started.removeServiceConnectors();}});//熟悉的地方// Start the server to trigger initialization listenersthis.tomcat.start();// We can re-throw failure exception directly in the main threadrethrowDeferredStartupExceptions();try {ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());}catch (NamingException ex) {// Naming is not enabled. Continue}// Unlike Jetty, all Tomcat threads are daemon threads. We create a// blocking non-daemon to stop immediate shutdownstartDaemonAwaitThread();}catch (Exception ex) {stopSilently();destroySilently();throw new WebServerException("Unable to start embedded Tomcat", ex);}}
}

6、 初始化DispatcherServlet

tomcatServer启动时还没有初始化DispatcherServlet,9大组件都未初始化,等到有请求进来了,再去初始化DispatcherServletDispatcherServlet的初始化在onRefresh中:

//刷新
protected void onRefresh(ApplicationContext context) {//初始化initStrategies(context);
}//初始化
protected void initStrategies(ApplicationContext context) {//9大组件initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);
}

6.1、onRefrsh

onRefresh由父类FrameworkServletinitWebApplicationContext()调用,初始化逻辑此处不介绍。

protected WebApplicationContext initWebApplicationContext() {//web容器WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;....}if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed -> trigger initial onRefresh manually here.synchronized (this.onRefreshMonitor) {//关键刷新onRefresh(wac);}}if (this.publishContext) {// Publish the context as a servlet context attribute.String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}

6.2、调用栈

具体的调用堆栈如下:

由下至上可以看出:

  • NioEndpoint接受到请求,交由Processor处理。
  • 不同的协议是不同的Processor处理。此处是Http11Processor,处理请求,封装Request对象,然后交由Engine引擎
  • Engine解析Host
  • Host确定容器
  • 容器确定Wrappper,Wrapper就是Servlet的包装。此处就是DispatchServlet
  • 然后DispatchServlet初始化并分发请求。

spring-boot-mvc启动流程相关推荐

  1. Spring Boot的启动流程

    文章目录 Spring Boot Spring Boot概念 Spring Boot的启动流程 1. 构造SpringApplection的实例 2. 调用实例的run方法 Spring Boot启动 ...

  2. Spring Boot项目启动流程

    概述 用过Spring Boot的应该都知道,在项目启动入口的主类main()方法里,一句简简单单的 SpringApplication.run( ... ); 便开启了项目的启动运行之路. 本文我们 ...

  3. spring boot应用启动原理分析

    spring boot quick start 在spring boot 里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动的,不需要另外配置一个W ...

  4. java按需读取word文件_干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结...

    我(梦在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近发表的一篇文章<.NET CORE与Spring Boot编写控制台程序应有 ...

  5. 记一次 Spring Boot 项目启动卡住问题排查记录

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 陈凯玲 来源 | https://url.cn ...

  6. Spring Boot————应用启动时的监听机制测试

    引言 本文承接前面的<Spring Boot----Spring Boot启动流程分析>,主要测试一下ApplicationContextInitializer.SpringApplica ...

  7. java和asp.net core_干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结...

    目录 我(梦在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近发表的一篇文章<.NET CORE与Spring Boot编写控制台程 ...

  8. Spring Boot(18)---启动原理解析

    Spring Boot(18)---启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会 ...

  9. ASP.NET Core MVC 源码学习:MVC 启动流程详解

    前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...

  10. Spring Boot定制启动图案

    转载自 Spring Boot定制启动图案 启动图案 Spring Boot在启动的时候会显示一个默认的Spring的图案,对应的类为SpringBootBanner. .   ____       ...

最新文章

  1. echarts 默认显示图例_echarts图例组件点击显示功能(默认功能点击消失)
  2. iOS开发-编译出错 duplicate symbols for architecture x86_64
  3. 著名模拟鼠标点击软件小点点被收购
  4. mysql建表的字段类型和约束条件
  5. 三维模型 检索 代码_文章导读|一种基于拉普拉斯算子和联合贝叶斯模型的三维形状检索方法...
  6. ahk写入excel单元格_【进阶】Excel 自动化教程
  7. 2.3用卡诺图化简逻辑函数210807
  8. 二或四 通道USB数据采集卡如合通过CMI耦合,构成USB高速多通道数据采集卡
  9. python第三方库 invalid requirement_python第三方库安装出问题
  10. STM32L476入坑-3-新建工程并点亮LED灯
  11. 什么是CPU主频、外频、倍频?之间关系是?
  12. P1564 膜拜 题解
  13. The DELETE statement conflicted with the REFERENCE constraint
  14. Rancher 使用 NFS Storage Classes 进行动态 NFS 存储
  15. 【Windows】怎么查看CUDA版本?Conda命令安装和NVIDIA官网安装包安装的CUDA有何区别?nvcc -V和nvidia-smi获得的CUDA版本有何区别?如何指定CUDA版本?
  16. 2021最新Navicat15下载安装包
  17. Django 框架 要点
  18. 捷联惯导系统学习2.5(等效旋转矢量微分方程)
  19. 计算机培训方案范文,2015年软件培训方案模板
  20. 河南省安阳市谷歌高清卫星地图下载

热门文章

  1. openGauss Summit 2021 | 汇聚数据库创新力量 逐梦数字时代星辰大海
  2. 看图学习VMWare以及常见问题答疑(转)
  3. [990]Geohash算法原理及实现
  4. 网狐大联盟机器管理工具编译与使用
  5. Picked up _JAVA_OPTIONS: -Xmx900M”
  6. H3C交换机路由器配置命令大全
  7. 字模在c语言运行后出现乱码,C语言字模问题
  8. android 代码重启app
  9. ssh / sftp 远程登录出现:RSA host key for *.*.*.* has changed...的解决办法
  10. vue3解读—reactivity响应式实现