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顺序与初始化顺序相似。