Springboot内置容器原理

Springboot内置容器原理

springboot强大的地方就是,相比于传统spring架构,省去了很多繁杂的配置,其中一个就是springboot支持了内置容器,启动的时候框架层面帮我们初始化和启动容器,我们更多的关心代码和业务逻辑实现即可,那么它是如何支持内置容器的,以及内置容器是如何初始化和启动的,本篇文章将展开详细分析。

一、多容器使用和支持 Springboot支持三种内置容器,分别是Tomcat、Jetty和Undertow,默认是使用Tomcat,只需要引入相关依赖就能使用响应能力。

1.Tomcat tomcat是默认内置容器,只需要引入starter-web就引入了容器。

代码语言:javascript复制

org.springframework.boot

spring-boot-starter-web

2.Jetty 使用jetty时,需要从starter-web中排出tomcat容器,然后引入jetty容器。

代码语言:javascript复制

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-tomcat

org.springframework.boot

spring-boot-starter-jetty

3.Undertow 使用undertow和jetty一样,排出tomcat依赖,并引入undertow依赖即可。

代码语言:javascript复制

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-tomcat

org.springframework.boot

spring-boot-starter-undertow

二、容器配置 容器配置主要从三个配置类和一个后置处理器分析,我们逐个分析一下。

1.EmbeddedWebServerFactoryCustomizerAutoConfiguration 内嵌web容器自动配置定义了几个WebServerFactoryCustomizer:

代码语言:javascript复制@Configuration

@ConditionalOnWebApplication

@EnableConfigurationProperties(ServerProperties.class)

public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

@Configuration

@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })

public static class TomcatWebServerFactoryCustomizerConfiguration {

@Bean

public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(

Environment environment, ServerProperties serverProperties) {

return new TomcatWebServerFactoryCustomizer(environment, serverProperties);

}

}

@Configuration

@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })

public static class JettyWebServerFactoryCustomizerConfiguration {

@Bean

public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(

Environment environment, ServerProperties serverProperties) {

return new JettyWebServerFactoryCustomizer(environment, serverProperties);

}

}

@Configuration

@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })

public static class UndertowWebServerFactoryCustomizerConfiguration {

@Bean

public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(

Environment environment, ServerProperties serverProperties) {

return new UndertowWebServerFactoryCustomizer(environment, serverProperties);

}

}

@Configuration

@ConditionalOnClass(HttpServer.class)

public static class NettyWebServerFactoryCustomizerConfiguration {

@Bean

public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(

Environment environment, ServerProperties serverProperties) {

return new NettyWebServerFactoryCustomizer(environment, serverProperties);

}

}

} 该配置类定义了针对Tomcat、Jetty、Undertow和Netty几个WebServerFactoryCustomizer类型的bean,用于将容器配置绑定到容器(使用Netty容器需要额外做配置,并且会改变原生spring web能力,本篇不做分析),并且配置哪种容器取决于哪种依赖被引入进来,拿Tomcat为例,会注册一个TomcatWebServerFactoryCustomizer。

它是一个WebServerFactoryCustomizer,重写customize方法,根据环境信息和用户配置属性信息自定义配置ConfigurableTomcatWebServerFactory容器工厂:

代码语言:javascript复制@Override

public void customize(ConfigurableTomcatWebServerFactory factory) {

ServerProperties properties = this.serverProperties;

ServerProperties.Tomcat tomcatProperties = properties.getTomcat();

PropertyMapper propertyMapper = PropertyMapper.get();

propertyMapper.from(tomcatProperties::getBasedir).whenNonNull()

.to(factory::setBaseDirectory);

propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull()

.as(Duration::getSeconds).as(Long::intValue)

.to(factory::setBackgroundProcessorDelay);

customizeRemoteIpValve(factory);

propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive)

.to((maxThreads) -> customizeMaxThreads(factory,

tomcatProperties.getMaxThreads()));

propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive)

.to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));

propertyMapper.from(this::determineMaxHttpHeaderSize).whenNonNull()

.asInt(DataSize::toBytes).when(this::isPositive)

.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,

maxHttpHeaderSize));

propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull()

.asInt(DataSize::toBytes)

.to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));

propertyMapper.from(tomcatProperties::getMaxHttpPostSize).asInt(DataSize::toBytes)

.when((maxHttpPostSize) -> maxHttpPostSize != 0)

.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,

maxHttpPostSize));

propertyMapper.from(tomcatProperties::getAccesslog)

.when(ServerProperties.Tomcat.Accesslog::isEnabled)

.to((enabled) -> customizeAccessLog(factory));

propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull()

.to(factory::setUriEncoding);

propertyMapper.from(properties::getConnectionTimeout).whenNonNull()

.to((connectionTimeout) -> customizeConnectionTimeout(factory,

connectionTimeout));

propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)

.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));

propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)

.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));

customizeStaticResources(factory);

customizeErrorReportValve(properties.getError(), factory);

} 有两个点我们要思考一下:

● EmbeddedWebServerFactoryCustomizerAutoConfiguration如何初始化?

● WebServerFactoryCustomizer的customize逻辑如何调用?

先看第一个问题,虽然EmbeddedWebServerFactoryCustomizerAutoConfiguration上边加了@Configuration注解,但是我们之前一篇文章《@ComponentScan原理分析》有说到,应用启动的时候要么默认扫描启动类所在路径以及子路径,要么用户自己指定路径,那么如果没有做处理,外部引入的类路径是扫描不到的,包括框架层,那么就要思考如何将其初始化。搜索该配置类引用的地方,在spring.factories中有引用到:

从文件中可以看到其被EnableAutoConfiguration指向:

代码语言:javascript复制org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\ 这样就保证了在springboot应用启动的时候将该EmbeddedWebServerFactoryCustomizerAutoConfiguration实例化。

然后再看第二个问题,直接搜WebServerFactoryCustomizer的customize方法调用,可以看到被WebServerFactoryCustomizerBeanPostProcessor调用:

WebServerFactoryCustomizerBeanPostProcessor是一个BeanPostProcessor:

重写了postProcessBeforeInitialization方法在实例化后初始化之前调用:

代码语言:javascript复制@Override

public Object postProcessBeforeInitialization(Object bean, String beanName)

throws BeansException {

if (bean instanceof WebServerFactory) {

postProcessBeforeInitialization((WebServerFactory) bean);

}

return bean;

} 作用是拦截到WebServerFactory类型的bean,然后执行初始化之前的逻辑:

代码语言:javascript复制private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {

LambdaSafe

.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),

webServerFactory)

.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)

.invoke((customizer) -> customizer.customize(webServerFactory));

} 从BeanFactory中获取WebServerFactoryCustomizer列表,然后执行其customize方法,正如前边说的Tomcat、jetty和Undertow几种WebServerFactoryCustomizer。

这里只是搞清楚了WebServerFactoryCustomizer被谁调用,又引入了一个BeanPostProcessor,那么这个类处理器又是何时注册?由于涉及到另外一个配置类ServletWebServerFactoryAutoConfiguration,在对应模块再详细分析。

2.ServletWebServerFactoryConfiguration

springboot具体支持的内置容器是在ServletWebServerFactoryConfiguration定义,我看一下对应配置:

代码语言:javascript复制@Configuration

class ServletWebServerFactoryConfiguration {

@Configuration

@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

public static class EmbeddedTomcat {

@Bean

public TomcatServletWebServerFactory tomcatServletWebServerFactory() {

return new TomcatServletWebServerFactory();

}

}

@Configuration

@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,

WebAppContext.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

public static class EmbeddedJetty {

@Bean

public JettyServletWebServerFactory JettyServletWebServerFactory() {

return new JettyServletWebServerFactory();

}

}

@Configuration

@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

public static class EmbeddedUndertow {

@Bean

public UndertowServletWebServerFactory undertowServletWebServerFactory() {

return new UndertowServletWebServerFactory();

}

}

} 也是根据有没有引入相关依赖,注册对应类型容器的bean,并且返回的bean类型是WebServerFactory,拿TomcatServletWebServerFactory为例:

在应用启动时获取web容器是通过重写ServletWebServerFactory的getWebServer方法实现:

代码语言:javascript复制@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);

} 所以这里并没有实例化任何容器,而是创建了所支持的容器的工厂,也即是这里只是声明了创建容器的工厂bean。

并且ServletWebServerFactoryConfiguration中声明的几种容器创建工厂被另外一个配置类ServletWebServerFactoryAutoConfiguration通过@Import导入了,也就是其初始化时机依赖于后者。

3.ServletWebServerFactoryAutoConfiguration

该类是web容器工厂自动配置类,可以理解为整合了前两个配置的能力,看一下配置内容:

代码语言:javascript复制@Configuration

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)

@ConditionalOnClass(ServletRequest.class)

@ConditionalOnWebApplication(type = Type.SERVLET)

@EnableConfigurationProperties(ServerProperties.class)

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,

ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,

ServletWebServerFactoryConfiguration.EmbeddedJetty.class,

ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

public class ServletWebServerFactoryAutoConfiguration {

@Bean

public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(

ServerProperties serverProperties) {

return new ServletWebServerFactoryCustomizer(serverProperties);

}

@Bean

@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")

public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(

ServerProperties serverProperties) {

return new TomcatServletWebServerFactoryCustomizer(serverProperties);

}

} 首先通过@Import注解导入了一个ImportBeanDefinitionRegistrar类BeanPostProcessorsRegistrar以及三个容器配置类EmbeddedTomcat、EmbeddedJetty和EmbeddedUndertow。根据前边的分析,此处导入配置类会和当前配置类一起实例化,而BeanPostProcessorsRegistrar会在实例化之后被ConfigurationClassPostProcessor调用,参考《撩一撩ImportBeanDefinitionRegistrar》。

然后声明了两个WebServerFactoryCustomizer类型的bean;ServletWebServerFactoryCustomizer做一些通用的容器配置,比如端口、地址、上下文路径等等:

代码语言:javascript复制@Override

public void customize(ConfigurableServletWebServerFactory factory) {

PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();

map.from(this.serverProperties::getPort).to(factory::setPort);

map.from(this.serverProperties::getAddress).to(factory::setAddress);

map.from(this.serverProperties.getServlet()::getContextPath)

.to(factory::setContextPath);

map.from(this.serverProperties.getServlet()::getApplicationDisplayName)

.to(factory::setDisplayName);

map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);

map.from(this.serverProperties::getSsl).to(factory::setSsl);

map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);

map.from(this.serverProperties::getCompression).to(factory::setCompression);

map.from(this.serverProperties::getHttp2).to(factory::setHttp2);

map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);

map.from(this.serverProperties.getServlet()::getContextParameters)

.to(factory::setInitParameters);

} TomcatServletWebServerFactoryCustomizer是在针对容器是Tomcat时用于定制化 TomcatServletWebServerFactory,仅在org.apache.catalina.startup.Tomcat类在classpath路径下生效。

和前两个配置一样,ServletWebServerFactoryAutoConfiguration也是springboot引入的外部配置,@Configuration是无法主动被启动类扫描到,搜索可以看到spring.factories中有引用:

也是通过EnableAutoConfiguration方式引用,启动的时候实例化。

4.BeanPostProcessorsRegistrar

BeanPostProcessorsRegistrar在中定义并且导入:

代码语言:javascript复制public static class BeanPostProcessorsRegistrar

implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

private ConfigurableListableBeanFactory beanFactory;

@Override

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

if (beanFactory instanceof ConfigurableListableBeanFactory) {

this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;

}

}

@Override

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,

BeanDefinitionRegistry registry) {

if (this.beanFactory == null) {

return;

}

registerSyntheticBeanIfMissing(registry,

"webServerFactoryCustomizerBeanPostProcessor",

WebServerFactoryCustomizerBeanPostProcessor.class);

registerSyntheticBeanIfMissing(registry,

"errorPageRegistrarBeanPostProcessor",

ErrorPageRegistrarBeanPostProcessor.class);

}

private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,

String name, Class beanClass) {

if (ObjectUtils.isEmpty(

this.beanFactory.getBeanNamesForType(beanClass, true, false))) {

RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);

beanDefinition.setSynthetic(true);

registry.registerBeanDefinition(name, beanDefinition);

}

}

} 在ConfigurationClassPostProcessor调用registerBeanDefinitions时会注册两个bean,分别是WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor,后者用于处理错误页面,WebServerFactoryCustomizerBeanPostProcessor的话我们前边有提到,用于将WebServerFactoryCustomizer的配置应用到对应的容器工厂。

三、容器实例化与启动 通过第二节的分析,web容器配置已经准备完毕,那么我们就分析一下springboot应用启动时内嵌容器的实例化与启动。

springboot启动过程中会调用AbstractApplicationContext的refresh方法:

代码语言:javascript复制public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

// Prepare this context for refreshing.

prepareRefresh();

// Tell the subclass to refresh the internal bean factory.

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.

prepareBeanFactory(beanFactory);

try {

//...省略

// Invoke factory processors registered as beans in the context.

invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.

registerBeanPostProcessors(beanFactory);

//...省略

// Initialize other special beans in specific context subclasses.

onRefresh();

//...省略

finishRefresh();

}

//...省略

}

} invokeBeanFactoryPostProcessors会解析@Configuration注解并将对应信息注册成BeanDefinition,registerBeanPostProcessors方法会将BeanPostProcessor注册到BeanFactory中,其中就包括前边分析的WebServerFactoryCustomizerBeanPostProcessor,而创建和实例化web容器入口就在onRefresh方法,看一下子类ServletWebServerApplicationContext的继承关系以及onRefresh实现:

极其复杂的继承关系,本质上是一个BeanFactory,间接继承了AbstractApplicationContext。

代码语言:javascript复制@Override

protected void onRefresh() {

super.onRefresh();

try {

createWebServer();

}

catch (Throwable ex) {

throw new ApplicationContextException("Unable to start web server", ex);

}

} 然后调用私有方法创建web容器:

代码语言:javascript复制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();

} 启动流程尚未创建web容器,所以会走到if分支,获取web容器工厂,然后从工厂获取web容器,先看一下获取容器工厂:

代码语言:javascript复制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 ApplicationContextException(

"Unable to start ServletWebServerApplicationContext due to missing "

+ "ServletWebServerFactory bean.");

}

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);

} 从BeanFactory中获取ServletWebServerFactory类型的beanName信息(此时ServletWebServerFactory尚未实例化,但是BeanDefinition中有beanName),然后检查ServletWebServerFactory类型的BeanDefinition数量是合法。

● 如果数量为0,说明starter-web引入的默认tomcat容器依赖被排出,并且没有引入jetty或者undertow依赖。

● 如果数量大于1,说明starter-web没有排出tomcat容器依赖,并且同时引入了jetty或者undertow依赖。

然后调用BeanFactory的getBean方法将web容器工厂实例化并返回。接着看通过web容器工厂获取容器的实现(tomcat为例):

代码语言:javascript复制@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);

} 创建tomcat做一些初始化配置和准备工作(设置根目录、连接器、引擎和上下文等),接着调用getTomcatWebServer方法返回WebServer:

代码语言:javascript复制protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {

return new TomcatWebServer(tomcat, getPort() >= 0);

} 这里是通过Tomcat包装成spring规范的TomcatWebServer,继续看创建web容器:

代码语言:javascript复制public TomcatWebServer(Tomcat tomcat, boolean autoStart) {

Assert.notNull(tomcat, "Tomcat Server must not be null");

this.tomcat = tomcat;

this.autoStart = autoStart;

initialize();

} 然后调用initialize方法:

代码语言:javascript复制private void initialize() throws WebServerException {

synchronized (this.monitor) {

try {

addInstanceIdToEngineName();

Context context = findContext();

context.addLifecycleListener((event) -> {

if (context.equals(event.getSource())

&& Lifecycle.START_EVENT.equals(event.getType())) {

removeServiceConnectors();

}

});

// Start the server to trigger initialization listeners

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) {

}

startDaemonAwaitThread();

}

catch (Exception ex) {

stopSilently();

throw new WebServerException("Unable to start embedded Tomcat", ex);

}

}

} 获取容器上下文,添加生命周期监听器,启动tomcat容器,然后启动非守护线程避免立即关闭。

到这里,springboot内置web容器的启动并没有算完成,我们回到AbstractApplicationContext的refresh方法,看到try代码块最后一行调用finishRefresh,会调用到ServletWebServerApplicationContext的重写方法finishRefresh:

代码语言:javascript复制@Override

protected void finishRefresh() {

super.finishRefresh();

WebServer webServer = startWebServer();

if (webServer != null) {

publishEvent(new ServletWebServerInitializedEvent(webServer, this));

}

} 该方法调用私有方法startWebServer:

代码语言:javascript复制private WebServer startWebServer() {

WebServer webServer = this.webServer;

if (webServer != null) {

webServer.start();

}

return webServer;

} 基于前一步已经从web容器工厂获取到WebServer,比如TomcatWebServer,然后会调用WebServer的start方法:

代码语言:javascript复制@Override

public void start() throws WebServerException {

synchronized (this.monitor) {

if (this.started) {

return;

}

try {

addPreviouslyRemovedConnectors();

Connector connector = this.tomcat.getConnector();

if (connector != null && this.autoStart) {

performDeferredLoadOnStartup();

}

checkThatConnectorsHaveStarted();

this.started = true;

}

catch (ConnectorStartFailedException ex) {

stopSilently();

throw ex;

}

catch (Exception ex) {

throw new WebServerException("Unable to start embedded Tomcat server",

ex);

}

finally {

Context context = findContext();

ContextBindings.unbindClassLoader(context, context.getNamingToken(),

getClass().getClassLoader());

}

}

} 其实这一步的操作可以理解为web容器启动的检查和兜底,如果前边已经启动成功了直接返回,否则对于一些较旧的Servlet框架(例如Struts、BIRT)在此阶段使用线程上下文类加载器创建Servlet实例,然后检查如果启动失败则抛出异常给调用方。

整个web容器的实例化和启动流程图如下:

总结 基于springboot我们可以很便捷的构建和启动应用,默认情况下它帮我们内置了tomcat容器,在应用启动时我们可以完全感觉不到其存在,如果有一些特定场景需要切换其他容器,只需要在依赖维度做一下简单配置和替换就能解决。本篇文章我们从使用和源码原理维度详细的剖析了springboot内置容器的支持和原理,对于springboot启动时web容器的实例化和启动理解,以及在出现问题时的排查应该都会有比较大的帮助,比如如果应用启动时出现如下异常:

那么很容器就能知道是因为要么没有配置内置容器依赖,要么就是引入了多种内置容器。

相关推荐