在上一篇文章中,我们了解到了Spring是如何启动内置Web服务器的。我们也知道Spring Boot本身是使用Tomcat作为默认服务器的。这又是如何做到的呢?

我们来细看下EmbeddedWebApplicationContext中的createEmbeddedServletContainer方法。Spring Boot所支持的嵌入式服务器都是在这里初始化并创建的。我们可以看到其先获取了嵌入式服务器的工厂类,然后通过工厂创建了嵌入式服务器。

private void createEmbeddedServletContainer() {
		EmbeddedServletContainer localContainer = this.embeddedServletContainer;
		ServletContext localServletContext = getServletContext();
		if (localContainer == null && localServletContext == null) {
			//获取containerFactory 
			EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
			//通过containerFactory获取ServletContainer
			this.embeddedServletContainer = containerFactory
					.getEmbeddedServletContainer(getSelfInitializer());
		} else if (localServletContext != null) {
			try {
				getSelfInitializer().onStartup(localServletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context",
						ex);
			}
		}
		initPropertySources();
	}

其中决定使用哪种嵌入式服务器的关键在于getEmbeddedServletContainerFactory这个方法。

protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
		//首先获取已加载的实现EmbeddedServletContainerFactory接口的对象(不包括其本身及抽象类)
		String[] beanNames = getBeanFactory()
				.getBeanNamesForType(EmbeddedServletContainerFactory.class);
		//如果不存在 说明未存在对应实现
		if (beanNames.length == 0) {
			throw new ApplicationContextException(
					"Unable to start EmbeddedWebApplicationContext due to missing "
							+ "EmbeddedServletContainerFactory bean.");
		}
		//如果存在多个 则要求用户必须选其一
		if (beanNames.length > 1) {
			throw new ApplicationContextException(
					"Unable to start EmbeddedWebApplicationContext due to multiple "
							+ "EmbeddedServletContainerFactory beans : "
							+ StringUtils.arrayToCommaDelimitedString(beanNames));
		}
		return getBeanFactory().getBean(beanNames[0],
				EmbeddedServletContainerFactory.class);
	}

从这里我们可以看到Spring Boot仍然是使用获取EmbeddedServletContainerFactory实现的方式来进行嵌入式服务器的加载的。
可以看到在Spring Boot 1.5.2版本中这货的实现有三个,分别是:

  • JettyEmbeddedServletContainer
  • TomcatEmbeddedServletContainer
  • UndertowEmbeddedServletContainer

从这里可以看出来Spring Boot目前是支持这三种嵌入式服务器的。
那为什么默认的会是Tomcat呢?
我们查看下spring-boot-starter-web的源码(其实这货只有一个pom.xml)文件,可以看到其pom.xml包含了Tomcat的依赖的。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starters</artifactId>
		<version>1.5.2.RELEASE</version>
	</parent>
	<artifactId>spring-boot-starter-web</artifactId>
	<name>Spring Boot Web Starter</name>
	<description>Starter for building web, including RESTful, applications using Spring
		MVC. Uses Tomcat as the default embedded container</description>
	<url>http://projects.spring.io/spring-boot/</url>
	<organization>
		<name>Pivotal Software, Inc.</name>
		<url>http://www.spring.io</url>
	</organization>
	<properties>
		<main.basedir>${basedir}/../..</main.basedir>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<!-- 其内置了Tomcat依赖  -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
		</dependency>
	</dependencies>
</project>

那么spring-boot-starter-tomcat里面又是什么东西呢?这货也只有一个pom.xml文件,唯一的作用就是告诉Maven去下载Tomcat的嵌入式依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starters</artifactId>
		<version>1.5.2.RELEASE</version>
	</parent>
	<artifactId>spring-boot-starter-tomcat</artifactId>
	<name>Spring Boot Tomcat Starter</name>
	<description>Starter for using Tomcat as the embedded servlet container. Default
		servlet container starter used by spring-boot-starter-web</description>
	<properties>
		<main.basedir>${basedir}/../../..</main.basedir>
	</properties>
	<dependencies>
		<dependency>
			<groupId>javax.annotation</groupId>
			<artifactId>javax.annotation-api</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-core</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-annotations-api</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-el</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-websocket</artifactId>
		</dependency>
	</dependencies>
</project>

到这里我们也就基本搞清楚了Spring Boot启动嵌入式服务器的逻辑。因为spring-boot-starter-web自带了Tomcat的嵌入式依赖,所以仅有TomcatEmbeddedServletContainerFactory的创建是满足条件的。
所以使用默认配置的话,getEmbeddedServletContainerFactory()这个方法会返回为唯一可用的TomcatEmbeddedServletContainerFactory,进而使用它创建并启动Tomcat嵌入式服务。
个人由此猜测针对数据库等其他组件的检测应该也是采用了此类模式,即通过扫描是否特定Class的实例来决定是否启动特定服务。