系列文章目录和关于我

零丶引入

我在初学spring的时候,很懵逼,因为整个项目中不存在main方法,让我有点摸不着头脑。那时候我知道有个东西叫tomcat是它监听了端口,解析了协议调到了我的servlet。

在我初学SpringBoot的时候,很懵逼,有main方法了,但是tomcat在哪里呢,又是如何启动起来的?。

一丶原生tomcat启动流程

Tomcat总体架构,启动流程与处理请求流程中学习了tomcat总体架构和启动流程。

在springboot内嵌tomcat中则不再使用BootStrap->Catalina这种方式进行启动,而是跨过这一层直接启动了Server。

二丶SpringBoot根据上下文推断ApplicationContext类型

下图是SpringBoot的启动流程,其中红色框框是SpringBoot推断ApplicationContext的步骤

这是我们学习springboot内嵌tomcat启动的切入点。

1.推断当前webApplication类型

在springboot启动的时候会根据classpath推断当前web应用类型。如果在classpath中存在org.springframework.web.reactive.DispatcherHandler那么会视为REACTIVE类型,如果存在javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext那么会视为SERVLET类型

2.获取webApplication类型对应ApplicationContext

SpringBoot会使用ApplicationContextFactory去构建出一个ApplicationContext,一般默认使用DefaultApplicationContextFactory,DefaultApplicationContextFactory会从spring.factories中找出所有的ApplicationContextFactory实现类根据webApplication类型去创建

其中有SERVLET类型对应的AnnotationConfigServletWebServerApplicationContext内部类Factory和REACTIVE类型对应的AnnotationConfigReactiveWebServerApplicationContext内部类Factory。

在不使用spring 响应式编程的情况下这里都会使用AnnotationConfigServletWebServerApplicationContext内部类Factory去构建

也就是说SpringBoot的启动会使用SpringApplication构建出AnnotationConfigServletWebServerApplicationContext最为Spring上下文。

三丶AnnotationConfigServletWebServerApplicationContext刷新触发tomcat启动

在SpringBoot启动的过程中,SpringApplication会触发Spring上下文(ApplicationContext,也就是AnnotationConfigServletWebServerApplicationContext)的刷新

AnnotationConfigServletWebServerApplicationContext是AbstractApplicationContext的子类,其refresh刷新方法由AbstractApplicationContext进行了实现。大致流程如下

其中onRefresh钩子方法便会触发Tomcat的启动,onRefrsh由 AnnotationConfigServletWebServerApplicationContext父类ServletWebServerApplicationContext进行了实现

四丶createWebServer创建web服务器

下图是SpringBoot启动web服务器的全流程

1.ServletWebServerFactory

WebServerFactory是一个标记接口,ServletWebServerFactory中定义了方法getWebServer(ServletContextInitializer... initializers)来创建WebServer,如参ServletContextInitializer是函数式接口,具备方法onStartup来进行回调。

默认配置下SpringBoot将使用TomcatServletWebServerFactory来创建WebServer。

2.WebServer

WebServer是springboot对服务器的抽象,具备start,stop,getPort,shutDownGracefully(优雅停)方法。

3.创建TomcatWebServer流程

组装生成TomcatServer主要依赖于Tomcat这个类,Tomcat是嵌入式tomcat启动器,提供众多api来组装tomcat服务器。这一步其实就是在组装tomcat容器模型。

组装的过程依赖于Tomcat这个类提供的api

可以看到Tomcat中具备Server属性,这代表了web应用服务器

在Server中具备Service数组属性表示web应用服务器中众多的服务

这里只有一个服务——StandardService,其中包含Connector数组和Engine数组

  • Connector

    Connector负责监听客户端请求,返回响应数据。

    Connector内部由ProtocalHandler属性,其使用AbstractEndpoint监听端口,并将请求交给Processor处理

  • Engine

    Engine负责处理具体的请求。

    Engine是一个Container内部使用HashMap维护Host(key是hostName,value是Host对象)

    Host也是一个Container,内部同一使用HashMap维护Context(key是上下文名称,value是Context对象)

    最终将设置Tomcat对象到TomcatWebServer属性上,并调用Tomcat#start启动Tomcat服务器

五丶TomcatWebServer启动Tomcat

TomcatWebServer会调用Tomcat.start方法来启动Tomcat,整个启动流程和原生Tomcat一致

六丶DispatcherServlet是怎么被加到tomcat中的

SpringBoot中如果使用web-stater,那么会引入DispatcherServlet的自动装配,这也就是为什么Tomcat接收到的请求会来到DispatcherServlet,然后由DispatcherServlet反射调用到Controller的方法。

那么DispatcherServlet是什么时候被加入到Tomcat中的呢?

TomcatReactiveWebServerFactory#configureContext方法中会注册TomcatStarterTomcatEmbeddedContext

TomcatEmbeddedContextStandardContext的子类,在TomcatEmbeddedContext被调用start的时候,会拿出所有的ServletContainerInitializer调用其onStartup

这里便会拿到TomcatStarterTomcatStarter使用ServletContextInitializer数组记录了ServletWebServerApplicationContext#selfInitialize方法,从而实现ServletWebServerApplicationContext#selfInitialize的回调

SpringBoot之所以这么做是因为ServletContainerInitializer是Servlet规范接口,而ServletContextInitializerSpringBoot定义的接口,利用TomcatStarter将SpringBoot定义的接口嫁接到Servlet定义的规范中从而保证当用户将SpringBoot打包成war包也能触发ServletWebServerApplicationContext#selfInitialize

那么ServletWebServerApplicationContext#selfInitialize做了什么

private void selfInitialize(ServletContext servletContext) throws ServletException {
// <1> 将当前 Spring 应用上下文设置到 ServletContext 上下文的属性中
// 同时将 ServletContext 上下文设置到 Spring 应用上下文中
prepareWebApplicationContext(servletContext);
// <2> 向 Spring 应用上下文注册一个 ServletContextScope 对象(ServletContext 的封装)(这就是application这种bean作用域生效的本原因)
registerApplicationScope(servletContext);
// <3> 向 Spring 应用上下文注册 `contextParameters` 和 `contextAttributes` 属性
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
/**
* <4> 【重点】先从 Spring 应用上下文找到所有的 ServletContextInitializer
* 也就会找到各种 RegistrationBean,然后依次调用他们的 `onStartup` 方法,向 ServletContext 上下文注册 Servlet、Filter 和 EventListener
* 例如 DispatcherServletAutoConfiguration DispatcherServletRegistrationBean 就会注册 @link DispatcherServlet 对象
* 所以这里执行完了,也就启动了 Tomcat,同时注册了所有的 Servlet,那么 Web 应用准备就绪了
*/
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}

Spring会从BeanFactory拿到所有ServletContextInitializer的实现

注册其实都是回调这些ServletContextInitializer的onStartup方法,DispatcherServletRegistrationBean则会将DispatcherServlet注册到ServletContext中。

TomcatEmbeddedContext会将DispatcherServlet保证成Wrapper加入到TomcatEmbeddedContext中去。

七丶总结

这一波学习,让我深刻的理解了Tomcat容器模型,也了解到SpringBoot中使用Filter或者Servlet的时候,为什么要向Spring注入对应的RegsitrationBean,因为只有这样ServletWebServerApplicationContext才能从容器中获取到RegsitrationBean并注册到Tomcat中。

SpringBoot源码学习4——SpringBoot内嵌Tomcat启动流程源码分析的更多相关文章

  1. SpringBoot + Spring Security 学习笔记(二)安全认证流程源码详解

    用户认证流程 UsernamePasswordAuthenticationFilter 我们直接来看UsernamePasswordAuthenticationFilter类, public clas ...

  2. 【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码

    Zookeeper3.7源码剖析 能力目标 能基于Maven导入最新版Zookeeper源码 能说出Zookeeper单机启动流程 理解Zookeeper默认通信中4个线程的作用 掌握Zookeepe ...

  3. Android Activity启动流程源码全解析(1)

    前言 Activity是Android四大组件的老大,我们对它的生命周期方法调用顺序都烂熟于心了,可是这些生命周期方法到底是怎么调用的呢?在启动它的时候会用到startActivty这个方法,但是这个 ...

  4. Android Activity启动流程源码全解析(2)

    接上之前的分析 ++Android Activity启动流程源码全解析(1)++ 1.正在运行的Activity调用startPausingLocked 一个一个分析,先来看看startPausing ...

  5. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  6. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

  7. Spark(四十九):Spark On YARN启动流程源码分析(一)

    引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...

  8. Spring IOC 容器预启动流程源码探析

    Spring IOC 容器预启动流程源码探析 在应用程序中,一般是通过创建ClassPathXmlApplicationContext或AnnotationConfigApplicationConte ...

  9. SpringBoot启动流程源码分析

    前言 SpringBoot项目的启动流程是很多面试官面试中高级Java程序员喜欢问的问题.这个问题的答案涉及到了SpringBoot工程中的源码,也许我们之前看过别的大牛写过的有关SpringBoot ...

  10. springboot的启动流程源码分析

    .测试项目,随便一个简单的springboot项目即可: 直接debug调试: 可见,分2步,第一步是创建SpringApplication对象,第二步是调用run方法: 1.SpringApplic ...

随机推荐

  1. MFC 错误调试总结

    1.  msdia80.dll can not be loaded 该文件的路径应该是:C:\Program Files\Common Files\microsoft shared\VC\msdia8 ...

  2. 在 Sitecore 里使用 Solr 搜索 SortOrder 关联的 Item

    在 C# 使用 Solr 搜索 sitecore 的配置信息文件可直接丢进 <Instance>\App_Config 下,sitecore 会自动检测配置文件更新并加载到内存中. 通常情 ...

  3. CentOS7.6 单用户模式下修改root密码

    第一种方法: 1.启动时用上下键选择要进入的内核,输入'e'进入编辑 2.可以使用上下键移动找到linux16这行编辑ro 为 rw init=/sysroot/bin/sh 并使用ctrl + x进 ...

  4. Any to Any 实时变声的实现与落地丨RTC Dev Meetup

    前言 「语音处理」是实时互动领域中非常重要的一个场景,在「RTC Dev Meetup丨语音处理在实时互动领域的技术实践和应用」活动中,来自声网.微软和数美的技术专家,围绕该话题进行了相关分享. 本文 ...

  5. GRU简介

    一.GRU介绍 GRU是LSTM网络的一种效果很好的变体,它较LSTM网络的结构更加简单,而且效果也很好,因此也是当前非常流形的一种网络.GRU既然是LSTM的变体,因此也是可以解决RNN网络中的长依 ...

  6. java异常--自定义异常

    java异常--自定义异常 步骤: 创建自定义异常类. 在方法中通过throw关键字抛出异常对象. 处理异常try-catch 捕获并处理,否则在方法声明处通过throws关键字指明抛出给调用者的方法 ...

  7. MySQL8.0 创建用户及授权 - 看这篇就足够了

    什么时候会用到 对接外系统时,需要给其余系统开放访问权限 本系统中,分权限管理数据,防止root权限删库跑路 mysql版本 MySql8.0+ 具体步骤 1.命令行进入MySql 使用 mysql ...

  8. DevOps|研发效能价值如何衡量

    现在很多公司都在做或者计划做研发效能,也知道研发效能工作很重要,能提高产研运同学的协同效率,提高员工的工作效率和质量,提高业务交付效率和交付质量,但是价值有多大?效率又有多高呢?因为不容易说清楚,所以 ...

  9. vue之列表渲染v-for

    目录 简介 用法 v-for可循环的几种变量的展示 数组的循环展示 对象的循环展示 字符串的循环展示 数字的循环展示 v-for搭档key值的说明 js循环几种方式 基于索引的循环 数组的循环 数组基 ...

  10. ChatGPT,我彻彻底底沦陷了!

    当谈到人工智能技术的时候,我们会经常听到GPT这个术语.它代表"Generative Pre-trained Transformer",是一种机器学习模型,采用了神经网络来模拟人类 ...