logo头像

优秀是一种习惯

spring-boot嵌入式容器启动流程

本文于 364 天之前发表,文中内容可能已经过时。

嵌入式容器启动流程 author: Tank

#### spring boot 默认提供三种嵌入式容器 (ServletWebServerFactoryConfiguration)
1、Tomcat (常用于短连接,如有长连接场景尽量使用jetty)
1
2
3
4
5
6
7
8
9
10
@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();
}

}
2、Jetty (长连接场景居多)
1
2
3
4
5
6
7
8
9
10
11
@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();
}

}
3、Undertow (非阻塞容器)
1
2
3
4
5
6
7
8
9
10
11
@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();
}

}
三种容器默认是tomcat原因如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
		<!-- web 内默认引入的是tomcat  -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.0.4.RELEASE</version>
<scope>compile</scope>
</dependency>

<!-- 切换容器只需要将web内的tomcat依赖排除,加入其他容器依赖即可 如jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>2.0.4.RELEASE</version>
<scope>compile</scope>
</dependency>

ServletWebServerFactoryConfiguration 内三种容器是根据条件注入方式加载进来的。

启动流程

spring应用入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

//run方法
public ConfigurableApplicationContext run(String... args) {
try {
//省略代码。。。
//触发创建IOC
refreshContext(context);
//省略代码。。。
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
return context;
}

private void refreshContext(ConfigurableApplicationContext context) {

refresh(context);
//省略代码。。。
}

protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
//触发创建IOC
((AbstractApplicationContext) applicationContext).refresh();
}
AbstractApplicationContext 应用上下文的抽象处理类,也是IOC创建的处理类
1
2
3
4
5
6
7
8
9
10
11
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//省略代码....
onRefresh();
//省略代码....
}
}
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
//此方法为子容器创建类。他没有做任何处理。
}
ServletWebServerApplicationContext 创建servlet容器的处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Override
// 重写 AbstractApplicationContext onRefresh方法
protected void onRefresh() {
super.onRefresh();
try {
//创建web服务器
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

private void createWebServer() {
//首次创建时webServer肯定是为null
WebServer webServer = this.webServer;
//获取servlet上下文 还没有创建肯定为null 如果是外部容器会走else
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//所以第一次肯定会先进这里 先获取创建web服务器的工厂类
ServletWebServerFactory factory = getWebServerFactory();
//从服务器工厂类获取一个服务器 假如我们使用默认tomcat
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();
}

/**
* ServletWebServerFactory 所有子类如下
AbstractServletWebServerFactory 接口
ConfigurableServletWebServerFactory 接口
TomcatServletWebServerFactory 具体实现
JettyServletWebServerFactory 具体实现
UndertowServletWebServerFactory 具体实现
*/

//获取创建web服务器的工厂类
protected ServletWebServerFactory getWebServerFactory() {
// 从bean工厂里根据类型获取所有子类
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);
}
TomcatServletWebServerFactory 默认为tomcat 我们来看看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
//当上面调用getWebServer时 创建一个tomcat
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);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
//调用TomcatWebServer的有参构造去启动
return new TomcatWebServer(tomcat, getPort() >= 0);
}
TomcatWebServer Tomcat嵌入式容器启动类
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
private void initialize() throws WebServerException {
//省略代码....
//此处启动tomcat容器
this.tomcat.start();
//省略代码....

}