springboot强大的地方就是,相比于传统spring架构,省去了很多繁杂的配置,其中一个就是springboot支持了内置容器,启动的时候框架层面帮我们初始化和启动容器,我们更多的关心代码和业务逻辑实现即可,那么它是如何支持内置容器的,以及内置容器是如何初始化和启动的,本篇文章将展开详细分析。
一、多容器使用和支持
Springboot支持三种内置容器,分别是Tomcat、Jetty和Undertow,默认是使用Tomcat,只需要引入相关依赖就能使用响应能力。
1.Tomcat
tomcat是默认内置容器,只需要引入starter-web就引入了容器。
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.Jetty
使用jetty时,需要从starter-web中排出tomcat容器,然后引入jetty容器。
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
3.Undertow
使用undertow和jetty一样,排出tomcat依赖,并引入undertow依赖即可。
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
二、容器配置
容器配置主要从三个配置类和一个后置处理器分析,我们逐个分析一下。
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容器的实例化和启动理解,以及在出现问题时的排查应该都会有比较大的帮助,比如如果应用启动时出现如下异常:
那么很容器就能知道是因为要么没有配置内置容器依赖,要么就是引入了多种内置容器。