以前的web项目需要webapp目录且要打成war包,再外挂Tomcat才能成功部署,但Spring Boot并没有这些配置,他是如何做到的?
在 Spring Boot 自动配置及访问静态资源原理 一文中讲述及演示了Spring Boot访问非webapp下的资源,所以今天主要探讨Spring Boot是如何与Tomcat结合的。
首先,pom.xml中需要引入spring-boot-starter-web依赖,然后,依旧老套路,Debug走起,第一个断点:
org.springframework.boot.SpringApplication#run(java.lang.String…)
进入refreshContext() ,中间会调用refresh(),直到调用org.springframework.context.support.AbstractApplicationContext#refresh,
(看过spring源码的人应该觉得这个方法中的代码似曾相识) 进入onRefresh(),中间经过多步进入org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
调用了一个createWebServer(),顾名思义,创建webServer,点击去看一下做了什么事
点进去getWebServerFactory()
执行完后回到createWebServer(),继续下一步,先看getSelfInitializer()
继续看getWebServer()
这一段主要是创建tomcat相关,点进去prepareContext(),这个方法需要关注的逻辑如下:
然后进入configureContext()
执行完这个方法回到org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer中的getTomcatWebServer(tomcat)
/**
* Create a new {@link TomcatWebServer} instance.
* @param tomcat the underlying Tomcat server
* @param autoStart if the server should be started
*/
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
});
// Start the server to trigger initialization listeners
// 启动tomcat,并触发启动监听事件
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
执行这个方法的过程中可能会执行各种回调,执行完一系列操作后,回到org.springframework.context.support.AbstractApplicationContext#refresh中的finishRefresh()
在这个方法中会启动webServer,也就是启动Tomcat
到这里,内嵌tomcat的主要流程算是完成了,只是中间很多细节没有细讲,后续有时间再完善。。。