一、启动代码
@SpringBootApplication
@RestController
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
@GetMapping("/a")
public String a(){
return "111";
}
}
以spring boot的方式启动tomcat
二、源码分析
直接快进到refresh()方法
ServletWebServerApplicationContext类
@Override
protected void onRefresh() {
// 调用父类的onRefresh()方法
super.onRefresh();
try {
// 创建web容器 tomcat
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
// 初始为空
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
// 1、获取web容器工厂
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
// 2、获取web容器
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
// 获取web容器工厂
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new MissingWebServerFactoryBeanException(getClass(), ServletWebServerFactory.class,
WebApplicationType.SERVLET);
}
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
为什么springboot web应用默认web容器是tomcat?
在createWebServer方法的第一步获取web容器工厂,在不修改任何依赖的情况下,默认返回的
TomcatServletWebServerFactory,也就是默认使用tomcat容器,那么TomcatServletWebServerFactory是怎么被注入的呢?
要从springboot的自动装配说起,我们看到,在getWebServerFactory()方法中,是返回的类型为ServletWebServerFactory的bean对象,在最新的springboot3自动装配的文件org.springframework.boot.autoconfigure.AutoConfiguration.imports中有自动装配org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
ServletWebServerFactoryAutoConfiguration类片段
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration
@Import注解解析注入bean对象
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().toList());
factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().toList());
factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().toList());
return factory;
}
}
/**
* Nested configuration if Jetty is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedJetty {
@Bean
JettyServletWebServerFactory jettyServletWebServerFactory(
ObjectProvider<JettyServerCustomizer> serverCustomizers) {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().toList());
return factory;
}
}
/**
* Nested configuration if Undertow is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedUndertow {
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory(
ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.getDeploymentInfoCustomizers().addAll(deploymentInfoCustomizers.orderedStream().toList());
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().toList());
return factory;
}
@Bean
UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new UndertowServletWebServerFactoryCustomizer(serverProperties);
}
}
}
ServletWebServerFactoryConfiguration中的@Bean的对象就是web容器对应的ServletWebServerFactory了,EmbeddedTomcat类上面有@ConditionalOnClass,@ConditionalOnMissingBean注解,@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })就是该bean被注入的条件,在Servlet,Tomcat,UpgradeProtocol三个类都能被加载的情况下才能注入,即当前类路径下要存在这些类;@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)是当前spring容器中没有ServletWebServerFactory类型的bean时才注入该bean,防止同时启动多个web容器。
spring-boot-starter-web依赖项,这里是以springboot源码启动的,所以使用的构建工具是gradle
plugins {
id "org.springframework.boot.starter"
}
description = "Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container"
dependencies {
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-json"))
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-tomcat"))
// api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jetty"))
api("org.springframework:spring-web")
api("org.springframework:spring-webmvc")
我们看到spring-boot-starter-web默认依赖了spring-boot-starter-tomcat,所以默认使用的web容器是tomcat,要想使用别的web容器,直接更换依赖就可以了。