WebServer 初始化过程

在上一节中 Spring Boot 初始化了 WebServer 对应的工厂类。同时,我们也知道对应 Web容器的WebServer实现类有:TomcatWebServer、JettyWebServer和UndertowWebServer。

这节重点讲解这些 WebServer 是如何被初始化,又如何启动的。

WebServer 接口的源代码如下。

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

接口定义了 3 个方法: start 方 法为启动容器,stop 方 法为停止容器,getPort 方法为获得容器端口。

现在以 Tomcat 的启动为例来说明整个内置容器的加载与启动。在上节中,工厂类已经被自动配置初始化。那么,在什么地方用到它们的呢?这要回到最初 Spring Boot 启动的过程中。

还记得 SpringApplication 的 run 方法中有一个调用初始化容器的方法 refreshContext 吗?

我们就从这个方法开始追踪。

public ConfigurableApplicationContext run(String... args) {try {//初始化容器refreshContext(context);} catch (Throwable ex) {}

在 run 方法中调用了 refreshContext 方法,refreshContext 方法中又调用了refresh 方法。

private void refreshContext(ConfigurableApplicationContext context) {//调用 refresh 方法refresh(context);refresh 方法的代码如下。protected void refresh(ApplicationContext applicationContext) {Assert. isInstanceOf(AbstractApplicat ionContext. class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();}

通过 refresh 方法我们能看到什么呢?对的,就是 AbstractApplicationContext 这个抽象类,该类的实例化对象在调用 refreshContext 方法之前,已经通过 createApplicationContext 方法进行实例化了。createApplicationContext 方法的源代码如下。

protected ConfigurableApplicat ionContext createApplicationContext() {//首先获取容器的类变量Class> contextClass = this . applicat ionContextClass;/如果为 null,则根据 web 应用类型按照默认类进行创建if (contextClass == null) {trywitch (this . webApplicat ionType) {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 配置,则通过 Spring 提 供的 BeanUtils 来进行实例化 return(ConfigurableApplicationContext) BeanUtils. instantiateClass(contextClass);}}}

ServletWeb 项目默认会实例化 DEFAULT_ _SERVLET_ WEB_ _CONTEXT _CLASS 常量指定的 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 类。

在 refresh 方法中调用的 AbstractApplicationContext 的 refresh 方法就是这个常量配置的类的 refresh 方法但 AnnotationConfigServletWebServerApplicationContext 方法内并没有该refresh 方法,该方法定义在它的父类 ServletWebServerApplicationContext 中。

@Overridepublic final void refresh() throws BeansException, IllegalStateExceptiontry {super . refresh();} catch (RuntimeException ex) {stopAndReleaseWebServer();throw ex;}}

ServletWebServerApplicationContext 的 refresh 方 法 仅 调 用 了 父 类AbstractApplication-Context 中的 refresh 方法。AbstractApplicationContext 中的 refresh方法的代码如下。

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this . startupShutdownMonitor)try {...onRefresh();}catch (BeansException ex) {}

忽略掉 refresh 方法中的其他方法,我们重点了解下其调用的 onRefresh 方法。

protected void onRefresh() throws BeansException {//为子类提供,默认不做任何操作我们发现这个 onRefresh 方法默认是空的,待其子类来实现。也就是说,该方法真正的实现又回到了它的子类 ServletWebServerApplicationContext 中。@Overrideprotected void onRefresh() {super . onRefresh();try {createWebServer();} catch (Throwable ex){throw new ApplicationContextException("Unable to start web server", ex);}

经过一路的代码跟踪,终于回到重点方法: createWebServer 方法。

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) {initPropertySources();}}

在 ServletWebServerApplicationContext 的 createWebServer 方 法 中 , 初 始 化 时 默 认web-Server 和 servletContext 都为 null,因此直接进入第一个 if 判断中的业务逻辑。看一下get-WebServerFactory 都做 了些什么。

protected ServletWebServerFactory getWebServerFactory() {//使用 Bean name 数组的好处是可以不用考虑层级关系String[] beanNames = getBeanF actory() . getBeanNamesForType(ServletWebServer-Factory.class);if (beanNames.length == 0) {throw new ApplicationContextException("Unable to start ServletWebServe-rApplicationContext due to missing" +"ServletWebServerFactory bean.");if (beanNames . length > 1) {throw new ApplicationContextException("Unable to start ServletwebServer-ApplicationContext due to multiple”+ "ServletWebServerFactory beans :”+StringUtils.arrayToCommaDelimitedString(beanNames));}return getBeanFactory(). getBean(beanNames[0], ServletWebServerFactory.class);}

getWebServerFactory 方法中通过 BeanFactory 获得类型为 ServletWebServerFactory 类的 beanNames 数组,然后判断数组长度。当 beanNames 长度为 0 时,说明容器中没有对应的 Bean 存在,则抛出异常;当 beanNames 长度大于 1 时,说明存在多个对应的 Bean,也就是说有可能同时存在多个 Web 容器的工厂方法,同样抛出异常;只有 beanNames 长度等于 1 时,说明恰好存在一个对应的 Bean, 才会获取对应的 Bean 并返回。

如果一层层向上追溯 TomcatServletWebServerFactory 的类结构,我们就会发现,它先是继承了抽象类AbstractServletWebServerFactory,而抽象类AbstractServletWebServerFactory 又实现了口 ConfigurableServletWebServerFactory 接口ConfigurableServletWebServer-Factory又继承接口ServletWebServerFactory.这里获得的 ServletWebServerFactory 的具体实现类,正是我们在上一节中通过自动配置实例化的TomcatServletWebServerFactory 对象的 Bean 名称。

当获得 ServletWebServerFactory 之后,便调用了它的 getWebServer 方法,以 Tomcat 为例,其实也就是调用了 TomcatServletWebServerFactory 的 getWebServer 方法。

@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers)//内置 Tomcat 包中提供的类Tomcat tomcat = new Tomcat();//获取并没置 baseDir 路径,不存在则创建一个 ltomcat 为前缀的临时文件File baseDir = (this. baseDirectory != null) ? this. baseDirectorycreateTempDir("tomcat");tomcat . setBaseDir(baseDir . getAbsolutePath());/创建 ConnectorConnector connector = new Connector(this . protocol);tomcat . getService(). addConnector(connector);// Connector 定制化customi zeConnector(connector);tomcat. setConnector( connector);tomcat. . getHost(). setAutoDeploy(false);configureEngine(tomcat. getEngine());for (Connector additionalConnector : this . additionalTomcatConnectors) {tomcat . getService() . addConnector ( additionalConnector);prepareContext(tomcat . getHost(), initializers);//创建 omcatWebServerreturn getTomcatwebServer(tomcat);}}

TomcatServletWebServerFactory 的 getWebServer 方法中实现了 Tomcat 的创建、BaseDir的设置 、Connector 的初始化和定制化等一系 列初始化操作。

至此,上面代码中依旧没有 体现 TomcatServer 的创建和初始化, 不要着急,它们就在getWebServer 方法的最后- -行代码调用的 getTomcatWebServer 方法中。

protected TomcatWebServer getTomcatWebServer (Tomcat tomcat) {return new TomcatWebServer(tomcat, getPort() >=

getTomcatWebServer 方法的实现很简单,将 getWebServer 中创建的 Tomcat 对象和当前类中 port 值是否大于等于 0 的判断结果作为 TomcatWebServer 构造方法的参数传入,创建 TomcatWebServer 对象。

针 对 getTomcatWebServer 方 法 , 子 类 可 以 重 写 该 方 法 , 返 回 一 个 不 同 的Tomcat-WebServer 或者添加针对 Tomcat Server 的一些额外操作。

先看 TomcatWebServer 的构造方法源码。

public class TomcatWebServer implements WebServer {private final Tomcat tomcat;private final boolean autoStart;public TomcatWebServer(Tomcat tomcat, boolean autoStart) {Assert . notNull(tomcat, "Tomcat Server must not be null");this. tomcat = tomcat;this. autoStart = autoStart;initialize();}}

构造方法接收 Tomcat tomcat 和 boolean autoStart 两个参数,并将其赋值给对应的成员变量。其中 Tomcat 参数不能为 null, autoStart 参 数则根据端口是否大于等于 0 来决定是否启动服务。在构造方法的最后,调用了 nitialize 方法来进行初始化操作。

omcatWebServern 的 initialize 方法源代码如下。

public class TomcatWebServer implements WebServer {private final Tomcat tomcat;private final boolean autoStart;privatevolatile boolean started;logger. info("Tomcat initialized with port(s): ”+ getPortsDescription(fsynchronized (this .monitor) {// 将 实 例 id 添 加 到 tomcat 引 擎 名 字 中 , 格 式 为 “ 原 引 / 擎 名 字 实 例 id”Contextcontext=findContext()://添加生命周期的监昕事件context . addL ifecyclelistener((event) -> {if (context . equals(event. getSource()) && Lifecycle . START EVENT.equals(event . getType()))//移除 connector, 磅保当服务器启动时不会进行协议绑定})//启动服务,触发初始化监听this. tomcat . start();/可以直接在主线程中重新抛出失败异常, TomcatStarter 不存在或状态错误均会抛出异常rethrowDeferredStartupExceptions();try/绑定一个命名的 context.到类加载器(), getClass().getClassLoader());sLoader(context, context. getNamingTokencatch (NamingException ex)// 当命名不可用时(抛异常), 直接跳过并继续// 与 Jetty 不同, 所有 Tomcat 线程都是守护程序线程。创建一 一个阻止非守护程序止立即关闭startDaemonAwaitThread();} catch (Exception ex) {stopSilently();destroySilently();throw new WebServerException("Unable to start embedded Tomcat", e x);}}}}

通过以上源代码,可以看出在 TomcatWebServer 的 initialize 方法中做了以下操作:重命名tomcat 弓|擎名称、对 Context 添加生 命周期监听事件、启动服务触发初始化监听、检查TomcatStarter 对象是否存在及 Container 状态是否正确、绑定命名到类加载器、启动守护等待线程等。

至此,针对 Tomcat 的 TomcatWebServer 的初始化已经完成。关于其他 Web 容器的WebServer 初始化操作,读者可仿照本节的思路进行源代码分析,这里不再逐一讲解。

本文给大家讲解的内容是SpringBoot内置Servlet容器源码解析:WebServer初始化过程

  1. 下篇文章给大家讲解的是DispatcherServlet的加载过程;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

webserver接口_SpringBoot内置源码解析WebServer初始化过程相关推荐

  1. 8145v5 参数_SpringBoot外化配置源码解析:外化配置简介、参数处理|value|spring|调用|参数值

    SpringBoot外化配置源码解析 在前面章节我们讲解了 Spring Boot 的运作核心原理及启动过程中进行的一系列核心操作. 从本章开始,我们将针对在实践过程中应用的不同知识点的源代码进行解读 ...

  2. Spring 源码解析 - Bean创建过程 以及 解决循环依赖

    一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...

  3. 创建线程的第三种方式:实现Callable接口(含部分源码解析)

    创建线程的第三种方式--实现Callable接口 package com.lqy.Multithreading; import java.util.concurrent.Callable; impor ...

  4. Tomcat源码解析:初始化

    文章目录 Bootstrap init load Catalina load Digester init Server初始化 Service初始化 Engine初始化 Connector初始化 Pro ...

  5. Nginx源码分析-启动初始化过程(一)

    Nginx的启动初始化在src/core/nginx.c的main函数中完成,当然main函数是整个Nginx的入口,除了完成启动初始化任务以外,也必定是所有功能模块的入口之处.Nginx的初始化工作 ...

  6. CommandLineRunner与ApplicationRunner接口的使用及源码解析

    引言 我们在使用SpringBoot搭建项目的时候,如果希望在项目启动完成之前,能够初始化一些操作,针对这种需求,可以考虑实现如下两个接口(任一个都可以) org.springframework.bo ...

  7. python爬虫获取网站销售情况(内置源码)

    在现在这个信息爆炸的时代,要想高效的获取数据,爬虫是非常好用的.而用python做爬虫也十分简单方便,下面通过一个简单的小爬虫程序来看一看写爬虫的基本过程: 注:此处猫咪销售网站中的内容本来就可以免费 ...

  8. roaringbitmap java,roaringbitmap 源码解析 bitmap add过程

    最近在做标签平台的分析引擎.底层涉及到位图的处理,所以涉及到roaringbitmap.

  9. NCCL源码解析①:初始化及ncclUniqueId的产生

    作者|KIDGINBROOK 更新|潘丽晨 NCCL是英伟达开源的GPU通信库,支持集合通信和点对点通信. 看下官方给的一个demo: #include <stdio.h> #includ ...

最新文章

  1. python自定义随机数_python:numpy.random模块生成随机数
  2. 11个问题助你彻底搞懂工业互联网
  3. Linux下安装php环境并且配置Nginx支持php-fpm模块
  4. gevent -1073740791
  5. Quartz调度器学习--基本概念
  6. Python——Django学习笔记
  7. 之前安装的python环境可以删除吗-在Mac上删除自己安装的Python方法
  8. 孙丕恕离开浪潮 仪器厂历时60年成为服务器龙头企业
  9. java程序员面试需要注意什么_Java程序员面试时要注意的一些问题
  10. SpringCloud应用在Kubernetes上的最佳实践—开发部署
  11. mobi格式电子书_这几种电子书格式的关系与区别,资深Kindler有必要了解了解 !...
  12. php反向解析域名,域名系统 – Cloudflare,反向DNS和php邮件,最终在垃圾邮件文件夹中...
  13. status函数(自学数据结构第一天)
  14. PMBus中的PAGE
  15. android实体键盘输入法,推荐一个实体键盘专用输入法,是对 autotext的改进
  16. 卷积神经网络如何进行图像识别的
  17. MATLAB基本介绍(1)
  18. 轻松掌握MySQL数据库锁机制的相关原理
  19. 文件及代码代码命名规则
  20. 经典SQL查询语句大全

热门文章

  1. 如何创造出更优秀的用户体验?
  2. 数学是成就卓越开发人员的必备技能
  3. http协议与web本质
  4. 正方形矩阵求对角线之和
  5. Java 监听器,国际化
  6. 【代码笔记】iOS-翻书效果的实现
  7. Android TabHost中实现标签的滚动以及一些TabHost开发的奇怪问题
  8. 同步滚动两个DataGrid
  9. 十万服务器秒级管控 腾讯云如何将AIOps用于日常管理?
  10. grep/egrep和正则表达式汇总