我们肯定对SpringBoot默认内部集成了Tomcat web环境不陌生,虽然reactive容器逐渐崭露头角,但相信大部分的用户还是使用了Tomcat的容器的。不知道是否有同学和我一样,对SpringBoot如何集成了tomcat有好奇,这里我们从springboot的源码中查看一下,它是如何集成tomcat的;
先来看springboot的标准启动的代码:
SpringApplication.run(SpringBootApplication.class, args);
1. 首先,同名run方法中:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
可以找到创建了SpringApplication对象的代码,从构造方法我们可以找到一个关键点:
this.webApplicationType = WebApplicationType.deduceFromClasspath();
deduceFromClasspath方法会从环境里是否有特定的类来判断WebApplicationType,由于我们没有配置reactive,因此此处会返回WebApplicationType.SERVLET
2. 分析完构造方法之后,我们来看一下run方法的内部,其中有一行:
context = createApplicationContext();
这个方法就会根据上一步的WebApplicationType来决定上下文;在这里我们会执行到
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
3. webServer的创建入口
webServer的创建是在run方法的refreshContext中的。
经过几个跳转我们来到了十分熟悉的,十分出名的 AbstractApplicationContext # refresh方法,重点关注它的onRefresh
@Override
protected 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) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
这个方法先会创建一个webServer工厂类,默认情况下,就是TomcatServletWebServerFactory了!
我们看下它的getWebServer方法:
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
哈哈,成功找到了。
createWebServer接下来会设置webServer属性,最后调用initPropertySources方法(如果环境中存在servletContextInitParams/servletConfigInitParams属性,会对其进行一个替换),并不是关注的重点,我们先跳过;
4. 我们跟踪到AbstractApplicationContext # refresh -> finishRefresh方法:
@Override
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
这里会启动WebServer
private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.start();
}
return webServer;
}
在默认情况下,我们就会调用到TomcatWebServer方法了;