springboot内置tomcat启动源码详解

springboot 除了自动装配功能外,另一个重要的功能就是内置了 web 容器,默认是 tomcat。在启动 ioc 容器启动的过程中,会启动一个 web 容器。ioc 容器的启动,带动了 web 容器的启动。这也是 springboot 能够以 jar 包方式启动的原因。这根以前的 war 包方式相反,war 包方式启动是 web 容器启动,带动了 ioc 容器的启动。

本文详细讲下 ioc 启动时,启动 tomcat 容器的源码。

首先是入口:

前面的文章中讲过,refresh() 方法是一个模板方法,里面定义了一些空方法,使用者可以重写这些方法进行扩展。onRefresh() 方法就是一个提供给子类扩展的方法,它默认是空的。

springboot 的 ServletWebServerApplicationContext 对 onRefresh 进行了重写,在里面增加了 createWebServer 方法来启动 web 容器。

createWebServer 方法内部首先是创建并返回了一个 tomcatervletWebServerFactory,然后调用它的 getWebServer 方法来创建 Tomcat 对象。

这个 tomcatervletWebServerFactory 非常重要,它是一个工厂,用于创建 webServer 对象,用它来提供 web 服务。

先详细讲解下这个对象是从哪里获取的。这就涉及到 tomcat 相关组件的导入了,它也是通过 springboot 的自动装配机制导入的。

META-INF/spring.factories 的 EnableAutoConfiguration 中,有两个类是跟内置容器相关的,EmbeddedWebServerFactoryCustomizerAutoConfiguration 以及 ServletWebServerFactoryAutoConfiguration。

ServletWebServerFactoryAutoConfiguration 中导入了ServletWebServerFactoryConfiguration.EmbeddedTomcat 类,EmbeddedTomcat 里又配置了一个 bean 是 tomcatServletWebServerFactory。这个 bean 就是我们要找的。

根据前面的文章《springboot自动装配源码详解》可以知道,上面提到的类都会被加载。此时,这些类的定义信息已经都被加载了,但是还没被实例化和初始化(因为实例化是在 finishBeanFactoryInitialization 方法中做的,而 onRefresh 在这之前)。也就是说,在 beanDefinitionMap 和 beanDefinitionNames 中已经能找到了,其中就包括 tomcatServletWebServerFactory 这个 bean。

那么,根据 ServletWebServerFactory 类型,去 beanDefinitionNames 中寻找, 由于只有 tomcatServletWebServerFactory 是 ServletWebServerFactory 类型的,所以返回的是它。

从上图中也可以看到,它是循环遍历 beanDefinitionNames,来寻找 ServletWebServerFactory 类型的 bean 的。

目前获取了 tomcatervletWebServerFactory 这个 beanName,下面就要实例化这个 bean 了。

这个 getBean 之前讲过,就是实例化并初始化 bean 的地方。所以,在这个地方会将 tomcatServletWebServerFactory 在 ioc 容器中实例化出来。不过,这里可不是简单的实例化和初始化而已,它还在通过 BeanPostProcessor 对 bean 做了一些额外的事!

上面的思维导图截图中,提到了很多类,其中好几个以 Customizer 结尾,很明显是用来做定制化的。但是刚刚没讲它们,因为它们是在 tomcatServletWebServerFactory  bean 实例化完成后,初始化之前,才发挥作用的。那下面就详细讲解下它们是如何工作的。

在 bean 实例化完成,初始化之前,会调用所有 BeanPostProcessor 的前置方法 postProcessBeforeInitialization。而里面有一个 WebServerFactoryCustomizerBeanPostProcessor,它就是用来对这里的 tomcatServletWebServerFactory 做额外的处理的。

在它的 postProcessBeforeInitialization 方法中,首先获取 Customizers,也就是针对内置容器的定制器。就是通过在 ioc 容器中找 WebServerFactoryCustomizer.class 类型的 bean 定义信息。上面提到的几个 Customizers,在这里都能找到。

找到 Customizers 后,就分别调用它们的 customize 方法,来对 tomcatServletWebServerFactory 做定制化。

大概看看这些 Customizers 中做了什么。

里面主要还是设置一些 web 容器的属性。

现在已经知道 tomcatervletWebServerFactory 是怎么获取到的了。然后进 getWebServer 方法里看下。在里面能看到我们非常熟悉的东西。Service、Connector、Host、Engine 等,这些就是 tomcat 的组件,对应了传统 tomcat 的配置文件中的各种属性。这里只是以 java 对象的方式来实现。最终得到一个 Tomcat 对象。

这里看起来只是创建 Tomcat 对象,那么 tomcat 是在哪里启动的呢。答案是在 getTomcatWebServer 方法中。它返回一个 TomcatWebServer 对象,就是在 TomcatWebServer 的构造方法中,对 tomcat 进行了初始化,初始化时调用了 tomcat 的 start 方法启动 tomat 容器。

tomcat 启动之后,还有一步重要的操作:setDaemonAwaitThread

通过注释也可以看出,所有 tomcat 线程都是后台线程,正常执行完之后就 shutdown 了。

所以为了能让 tomcat 能一直保持运行着,需要创建一个阻塞的,非后台运行的线程来阻止它立即 shutdown!

这个额外的线程,deamon 设置为 false,运行此线程时,tomcat 会一直等待,只要没有一个明确的 shutdown 指令过来,tomcat 就不会停止!

到这边为止,tomcat 容器就算是启动成功了。那么有个疑问,平时我们启动 tomcat 完成后,不是会打印 Tomcat started on port(s): xxxx 吗?这里怎么没有见到呢?

我在 TomcatWebServer 类中,找到了这行注释,

通过在此方法中打上断点,看到了调用链,找到了一个关键的方法:finishRefresh

finishRefresh 也是在 refresh() 方法中的

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();

它位于 ioc 容器初始化 bean 之后,因为前面虽然 tomcat 已经启动了,但是 ioc 容器还没初始化好恩,要等所有 bean 全部实例化,并初始化好之后,tomcat 才能正式提供服务。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值