Springboot内置Tomcat启动过程
最近工作中需要在Tomcat端口启动后立即获取随机的端口号,所以研究了下Springboot相关的源码,在此记录下。
1. maven dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
Here is the dependency hierarchy of spring-boot-starter-web:

2. 启动入口
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// step1: create context
// 实例化AnnotationConfigServletWebServerApplicationContext类
context = createApplicationContext();
// step2: prepare context
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// step3: refresh context
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
2.1 create context
利用反射创建 AnnotationConfigServletWebServerApplicationContext类的实例,但

2.2 refresh context


这里的applicationContext就是createApplicationContext()方法创建的AnnotationConfigServletWebServerApplicationContext实例,但AnnotationConfigServletWebServerApplicationContext没有实现refresh()方法,它的父类
ServletWebServerApplicationContext中定义了该方法

最终调用到了AbstractApplicationContext类的refresh()方法
附:AnnotationConfigServletWebServerApplicationContext & ServletWebServerApplicationContext & AbstractApplicationContext的继承关系

AbstractApplicationContext类的refresh 方法,如下图:
重点关注两个方法:onRefresh() & finishRefresh()

2.2.1 创建web server
onRefresh()
根据java语言的多态性,会调用子类ServletWebServerApplicationContext的onRefresh(),
至此,看到了createWebServer(),这就是tomcat开始创建的入口。为了更好的理解内置Tomcat的启动,该部分放到后面仔细研究。
2.2.2 publish event
finishRefresh()

getLifecycleProcessor()返回的是DefaultLifecycleProcessor, onRefresh方法调用了startBeans方法

下面是从startBeans方法开始到WebServerStartStopLifecycle.start()方法的调用类图

源码:

LifecycleGroup是DefaultLifecycleProcessor的内部类,该内部类维护了Lifecycle beans的启动和停止;
因为这些有声明周期属性的beans的启动和停止需要按照一定的规则及顺序执行,所以需要用一个类封装这些复杂的属性和规则。

内部类的start()方法调用了外部类中的方法doStart():

doStart()方法调用了bean的start方法,对于WebServerStartStopLifecycle的start()方法如下:
真正开启tomcat 并开始监听端口
发布ServletWebServerInitializedEvent事件,我们可以通过监听该事件来获取tomcat的随机端口号。






下面就是端口号的获取和绑定

至此,我们了解了embedded tomcat的在springboot中context中的启动流程。下面回头关注embedded tomcat启动的源码
3. 创建Embedded Tomcat
在章节 2.3.1 onRefresh() 中,看到了tomcat创建的方法:ServletWebServerApplicationContext.onRefresh().

Tomcat的启动分为两步init 和 start, 会对应到源码的两个方法: init() 和 startInternal()。
3.1 overview
对照图中的组件之间的关系来分析源码更轻松
图1: 来自CSDN课程:

图2:类图

所有组件都需要init & start & destroy & stop, 故使其继承抽象类 LifecycleBase ,在LifecycleBase中有init() & start()方法,

start() => init() => initInternal()
=> startInternal()
initInternal()方法 & startInternal() 在该抽象类中是一个抽象方法,各个组件会实现自己的.
在子类中实现的startInternal要 state为 STARTING,状态的更新会触发START_EVENT

3.2 从获取ServletWebServerFactory开始
在createWebServer方法中,首先获取ServletWebServerFactory。 这个bean在context中可以获取到

有三个实现类,我们使用的是TomcatServletWebServerFactory。该方法只是获取到一个paused web server的实例,只有在ApplicationContext在完全刷新后,才能connect to 这个server.

ServletWebServerFactory 是用来创建embedded Webserver的:

3.3 创建web server


重点关注 tomcat.start()

此时,已经创建了server,所以getServer()直接返回之前创建的server. 这个重点看 server.start()

3.3.1 初始化
先关注这个init()方法, 这个方法里面会有server中一系列组件的init;
Flowchart
源码



调用子类自己的initInternal()方法,service中包含三个组件的初始化:
- engine
- executor
- connector



3.3.2 启动
Flowchart
类图

源码入口

然后开始进行server中各个组件的start,start顺序与初始化顺序相似。
本文详细解析了Spring Boot中如何利用内置Tomcat启动过程,并介绍了如何在启动后立即获取随机端口。内容涉及依赖管理、启动入口、嵌入式Tomcat创建和生命周期关键步骤,以及端口绑定原理。

1150

被折叠的 条评论
为什么被折叠?



