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

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 方法为获得容器端口。

245cf63796d584d0b79dd93feadae2ae.png

现在以 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 方法来进行初始化操作。

412d3eb4e37ca23687fe2fc3e6bdb4d2.png

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 初始化操作,读者可仿照本节的思路进行源代码分析,这里不再逐一讲解。

f6ec8dde371ea4c3f396b8ea5ffde971.png

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/455366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

提升应用程序弹性:保障工作负载正常运行

通过集群化、复制、快照、微服务和应用程序设计来提高企业工作负载的应用程序弹性和可用性。 应用程序的弹性和可用性是现代企业工作负载的关键属性。应用程序需要在硬件故障发生后,扛过服务故障(例如负载平衡器和域名系统错误)保持工作状态,并且可以忍受…

JDBC笔记01-JDBC,Connection,Statement,ResultSet,PreparedStatement,Properties

学习目标 理解JDBC原理 掌握Connection接口的使用 掌握Statement接口的使用 掌握ResultSet接口的使用 掌握PreparedStatement接口的使用 掌握Properties类与配置文件的使用 JDBC 概念 JDBC (Java DataBase Connectivity) Java数据库连接技术的简称,提供连接各种常…

NVDKC6416平台H.264算法优化

本文转载自:http://blog.csdn.net/embedesign/archive/2009/09/15/4556486.aspx,版权归原作者,编辑:小乙哥 多媒体通信终端设备具有广泛的应用前景,可以应用于视频会议、可视电话、PDA、数字电视等各个领域&#xff0…

拦截器及 Spring MVC 整合

一、实验介绍 1.1 实验内容 本节课程主要利用 Spring MVC 框架实现拦截器以及 Spring MVC 框架的整合。 1.2 实验知识点 Spring MVC 框架拦截器1.3 实验环境 JDK1.8Eclipse JavaEE二、实验步骤 2.1 拦截器实现 在项目 hrms 的目录 src/main/java 下新建包 com.shiyanlou.interc…

高德地图轨迹回放_高德地图上线了一个新功能….

文、路人甲TM德地图这两天刚上线了一个叫做「家人地图」的功能,所谓家人地图顾名思义,就是你可以通过高德地图组建一个家人圈,在这个圈子里面你可以看到你的家人在什么位置,当你的家人到达什么位置的时候自动发送通知或者警告&…

You have new mail in /var/spool/mail/root消除提示的方法

有时在进入系统的时候经常提示You have new mail in /var/spool/mail/root 你觉得烦人---解决方法: 修改系统配置文件/etc/profile,告诉系统不要去检查邮箱. 具体操作:命令行输入:echo "unset MAILCHECK" >> /etc…

c3p0-config.xml文件简单说明与备忘

<?xml version"1.0" encoding"UTF-8"?> <c3p0-config><named-config name"mysql"><!-- 配置数据库用户名 --><property name"user">root</property><!-- 配置数据库密码 --><property…

python 消息队列 get是从队首还是队尾取东西_从零开始Python对redis作为消息队列的使用...

一、Redis 服务1、安装yum install redis2、 python安装支持模块/opt/python2.7.13/bin/pip install redis3、 和redis的简单直接交互In [1]: import redisIn [2]: rc redis.Redis(host192.168.8.237,port6379,decode_responsesTrue)In [5]: rc.set(imoocc,jeson)Out[5]: True…

‘’和“”

单引号引的数据是char类型的 双引号引的数据是String类型的 单引号只能引一个字符 而双引号可以引0个及以上 字符&#xff08;Character&#xff09;是指人类语言的最小的表义符号&#xff0c;字符是指计算机中使用的字母、数字和符号&#xff0c;包括1、2、3、A、B、C、#、&am…

Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)

转载&#xff1a;http://www.cnblogs.com/jiafuwei/p/6145280.html 单个Quartz实例能给予你很好的Job调度能力&#xff0c;但它不能满足典型的企业需求&#xff0c;如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job&#xff0c;Quartz集群势必成为你…

JDBC笔记02-数据库连接池 Spring JDBC

今日内容 数据库连接池Spring JDBC : JDBC Template 数据库连接池 概念: 其实就是一个容器(集合),存放数据库连接的容器 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器 好处:…

20温控f1什么意思_欧姆龙温控器是什么 欧姆龙温控器介绍【图文】

欧姆龙温控器&#xff0c;乍一眼看上去真的很难理解这到底是一个什么产品&#xff0c;或者是一个有什么用处的温控器&#xff0c;对于这个比较浅显的问题&#xff0c;不知道大家会有什么样地感受&#xff0c;是不是正在一头雾水的等着我进行解答呢?经过我比较浅显的分析&#…

bootstrap 解决弹出窗口(modal) 常见问题

无法使用键盘esc关闭窗口方法&#xff1a; 首先在modal容器的div中增加属性tabindex"-1"&#xff0c;其次设置键盘ESC属性keyboard为true&#xff1b; 方法1&#xff1a;使用js打开窗口时 $(“#modal”).modal({keyboard:true}) 方法2&#xff1a;使用H5属性在modal窗…

zabbix3.2学习笔记(二):服务端安装

2019独角兽企业重金招聘Python工程师标准>>> 一般小公司大多将zabbix web端和zabbix server部署在同一台主机上&#xff0c;其实二者是可以分开的&#xff0c;web GUI配置连接到对应的数据库就行&#xff0c;让zabbix server和MySQL数据库在同一台主机上便于数据快速…

H.264解码器中CAVLC码表查找算法的分析与优化

0 引言 近年来&#xff0c;随着信息技术飞速发展和互联网的日益普及&#xff0c;尤其是以视频为信息主要来源的多媒体领域越来越受到人们的关注。H&#xff0e;264是ITU-T的视频编码专家组(VCEG)和ISO&#xff0f;IEC的活动图像编码专家组(MPEG)的联合视频组(Joint Video Te…

python求加速度_如何利用Python 为自然语言处理加速度

自去年发布 Python 的指代消解包(coreference resolution package)之后&#xff0c;很多用户开始用它来构建许多应用程序&#xff0c;而这些应用与我们最初的对话应用完全不同。利用 spaCy 和一点点 Cython 给 NLP 加速。自去年发布 Python 的指代消解包(coreference resolutio…

druid.properties文件的配置

# druid.properties文件的配置 driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://127.0.0.1:3306/plan usernameroot password # 初始化连接数量 initialSize5 # 最大连接数 maxActive10 # 最大超时时间 maxWait3000

jquery+easyui开发、培训文档

目 录 1.... Accordion&#xff08;可折叠标签&#xff09;....................................................................................... 2 1.1 实例.............................................................................................…

HAProxy用法详解 全网最详细中文文档

一、HAProxy简介&#xff08;1&#xff09;HAProxy 是一款提供高可用性、负载均衡以及基于TCP&#xff08;第四层&#xff09;和HTTP&#xff08;第七层&#xff09;应用的代理软件&#xff0c;支持虚拟主机&#xff0c;它是免费、快速并且可靠的一种解决方案。 HAProxy特别适用…

mp4文件格式系列

mp4文件格式系列1 - 综述Overview and Introduction Core Concepts MP4文件格式中&#xff0c;所有的内容存在一个称为movie的容器中。一个movie可以由多个tracks组成。每个track就是一个随时间变化的媒体序列&#xff0c;例如&#xff0c;视频帧序列。track里的每个时间单…