Springboot内置容器原理

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

一、多容器使用和支持

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

1.Tomcat

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.Jetty

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

<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依赖即可。

<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:

@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容器工厂:

@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指向:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\

    这样就保证了在springboot应用启动的时候将该EmbeddedWebServerFactoryCustomizerAutoConfiguration实例化。
然后再看第二个问题,直接搜WebServerFactoryCustomizer的customize方法调用,可以看到被WebServerFactoryCustomizerBeanPostProcessor调用:
在这里插入图片描述

    WebServerFactoryCustomizerBeanPostProcessor是一个BeanPostProcessor:
在这里插入图片描述

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

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException {
	if (bean instanceof WebServerFactory) {
		postProcessBeforeInitialization((WebServerFactory) bean);
	}
	return bean;
}

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

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定义,我看一下对应配置:

@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方法实现:

@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容器工厂自动配置类,可以理解为整合了前两个配置的能力,看一下配置内容:

@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做一些通用的容器配置,比如端口、地址、上下文路径等等:

@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在中定义并且导入:

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方法:

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。

@Override
protected void onRefresh() {
	super.onRefresh();
	try {
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}

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

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容器,先看一下获取容器工厂:

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为例):

@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:

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
	return new TomcatWebServer(tomcat, getPort() >= 0);
}

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

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
	Assert.notNull(tomcat, "Tomcat Server must not be null");
	this.tomcat = tomcat;
	this.autoStart = autoStart;
	initialize();
}

    然后调用initialize方法:

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:

@Override
protected void finishRefresh() {
	super.finishRefresh();
	WebServer webServer = startWebServer();
	if (webServer != null) {
		publishEvent(new ServletWebServerInitializedEvent(webServer, this));
	}
}

    该方法调用私有方法startWebServer:

private WebServer startWebServer() {
	WebServer webServer = this.webServer;
	if (webServer != null) {
		webServer.start();
	}
	return webServer;
}

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

@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容器的实例化和启动理解,以及在出现问题时的排查应该都会有比较大的帮助,比如如果应用启动时出现如下异常:
在这里插入图片描述

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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值