Spring boot-嵌入式web server启动过程分析(2)
2018-07-11

前言

本文是对上篇博文的继续,会以Jetty为例详细讲解一下。
在spring boot中嵌入Jetty最核心的三个类是ServletWebServerApplicationContextJettyServletWebServerFactoryJettyWebServer。上文中我们讲到ServletWebServerApplicationContext会在自己的生命周期中绑定create、start以及stop WebServer等操作。

首先我们来看一下在ServletWebServerApplicationContext中是如何选择ServletWebServerFactoryWebServer类型的。

选择ServletWebServerFactory及WebServer

本文主要讲述在ServletWebServerApplicationContextcreateServer这一步是如何选择以及创建Jetty webServer的。详细可以看一下方法createWebServer()。分两步:
1)确定ServletWebServerFactory;2)由确定ServletWebServerFactory实例化webServer。

1
2
3
4
5
6
7
8
9
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
//...... ignore
}

选择ServletWebServerFactory

通过调用getWebServerFactory()方法获取到JettyServletWebServerFactoryTomcatServletWebServerFactory等。获取的方式是调用getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class),是从context里边的beanFactory中获取类型为ServletWebServerFactory的bean,并且还需要保证只能有一个该类型的Bean。

1
2
3
4
5
6
protected ServletWebServerFactory getWebServerFactory() {
String[] beanNames = getBeanFactory()
.getBeanNamesForType(ServletWebServerFactory.class);
// ...... Check the length of beanNames, if length != 1, throw exception.
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

那么接下来的问题就是:这个类型为ServletWebServerFactory的bean是何时注册到beanFactory中的呢?

核心就在于两个类ServletWebServerFactoryConfigurationConfigurationClassPostProcessor。前者是一个典型的spring configuration类,带有@Configuration的注解,用于对Servlet WebServer配置。后者是一个BeanFactoryPostProcessor,主要用于启动阶段对@Configuration的类进行处理。

ServletWebServerFactoryConfiguration类

ServletWebServerFactoryConfiguration来自于jar包spring-boot-autoconfigure.查看类的源码可以看到提供了三个webServer的配置Tomcat、Jetty和Undertow。对应有三个加有@Configuration注解的类EmbeddedTomcatEmbeddedJettyEmbeddedUndertow

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
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会被在BeanFactory中被注册。能够达到这个效果主要依赖于两个配置@ConditionalOnClass@ConditionalOnMissingBean。前者的作用是集合里边的class都存在的时候,才会允许Bean注册至beanFactory;后者的作用表示ApplicationContext中不存在集合中的class时,允许Bean注册至beanFactory。

因此想要选择Jetty或者Tomcat作为webServer,可以通过添加或者删除指定webServer的jar包依赖来实现。

ConfigurationClassPostProcessor类

ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor,该类负责处理所有添加@Configuration,会将classpath下所有注解@Configuration的class类转化成BeanDefinition加入到beanFactory中。所有的BeanFactoryPostProcessor会在OnRefresh()方法中最开始的地方执行。因此在createServer执行的时候类型为JettyServletWebServerFactory的Bean已经存在于BeanFactory中。

生成JettyWebServer

上一步getWebServerFactory返回的会是JettyServletWebServerFactory。在JettyServletWebServerFactory的调用getWebServer会返回一个JettyWebServer实例。

创建&配置WebServer

这一节主要会用来讲解方法调用getWebServer(ServletContextInitializer... initializers)。一方面分析getWebServer方法的究竟干了哪些事情;另一方面会分析一下方法参数ServletContextInitializer,对应的实参是selfInitialize(ServletContext servletContext),目的是在webServer执行之前调用该方法执行一些初始化的操作。

getWebServer方法分析

下面是JettyServletWebServerFactorygetWebServer方法的源码,可以看到创建Jetty Server的整体流程。

  1. 创建 Jetty WebAppContext

  2. 创建 Jetty Server

  3. 将 ServletContextInitializer 配置到Jetty WebAppContext,后续Jetty WebAppContext初始化时会调用这些ServletContextInitializer.

  4. 将Jetty WebAppContext 封装配置成 Jetty Server的Handler

  5. 个性化的配置处理

  6. New JettyWebServer,封装上边创建好的Jetty Server。

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
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1.创建 Jetty WebAppContext
JettyEmbeddedWebAppContext context = new JettyEmbeddedWebAppContext();
int port = (getPort() >= 0 ? getPort() : 0);
InetSocketAddress address = new InetSocketAddress(getAddress(), port);
// 2.创建 Jetty Server
Server server = createServer(address);
// 3.将 ServletContextInitializer 配置到Jetty WebAppContext,后续Jetty WebAppContext初始化时会调用这些ServletContextInitializer.
configureWebAppContext(context, initializers);
// 4.将Jetty WebAppContext 封装配置成 Jetty Server的Handler
server.setHandler(addHandlerWrappers(context));
this.logger.info("Server initialized with port: " + port);
// 5.个性化的配置处理
if (getSsl() != null && getSsl().isEnabled()) {
customizeSsl(server, address);
}
for (JettyServerCustomizer customizer : getServerCustomizers()) {
customizer.customize(server);
}
if (this.useForwardHeaders) {
new ForwardHeadersCustomizer().customize(server);
}
// new JettyWebServer,封装上边创建好的Jetty Server。
return getJettyWebServer(server);
}

protected JettyWebServer getJettyWebServer(Server server) {
return new JettyWebServer(server, getPort() >= 0);
}

上边这一步,我们可以看到Jetty核心三组件Server、Handler(及其封装的AppContext)、Connector(在第2步

这个Jetty Server是怎么跟我们Spring boot创建的```WebApplicationContext```整合到一起去的呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

答案的关键就在于方法的参数`ServletContextInitializer`,对应的实参是`ServletWebServerApplicationContext`的`selfInitialize`方法。下面2.2一节会来分析一下这个方法。而至于`new JettyWebServer(server, getPort() >= 0)`构造函数中执行了什么,会在第3.1节中分析。

## selfInitialize方法

分析一下`selfInitialize`方法的源码,在实际调用中方法参数`servletContext`会是上一节创建的`JettyEmbeddedWebAppContext`的ServletContext。

```java
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
beanFactory);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
getServletContext());
existingScopes.restore();
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
getServletContext());
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}

关键看一下方法执行的第一行命令prepareWebApplicationContext(servletContext),具体方法的源码就不在此处贴了,大致解释一下这个方法执行的核心逻辑就是将当前的ServletWebServerApplicationContext注入到servletContextROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中去。至此我们也可以找到上一小节问题的答案。

方法的后边的命令registerWebApplicationScopes起到以下作用:

Register web-specific scopes (“request”, “session”, “globalSession”, “application”) with the given BeanFactory, as used by the WebApplicationContext.

命令registerEnvironmentBeans的作用是:

Register web-specific environment beans (“contextParameters”, “contextAttributes”) with the given BeanFactory, as used by the WebApplicationContext.

JettyWebServer

JettyWebServer 是对Jetty Server的一层包装,用来处理对Jetty Server的创建、启动和停止操作。

创建

JettyWebServer的创建是通过调用其构造函数来完成的。可以看到构造函数的核心部分是方法initialize()

1
2
3
4
5
6
public JettyWebServer(Server server, boolean autoStart) {
this.autoStart = autoStart;
Assert.notNull(server, "Jetty Server must not be null");
this.server = server;
initialize();
}

重点来看一下方法initialize(),逐行代码分析一下。

  1. 首先将Jetty Server的connectors赋值给this.connectors。为什么这么做呢?是因为后边会将Jetty Server的connectors清空,在此处先把这些cache起来,不然一会清空了就找不回来了。
  2. 其次调用Jetty Server的addBean()方法,给Jetty添加一个AbstractLifeCycle的Bean,熟悉Jetty源码的会知道这个Bean的doStart方法会在Jetty Server的start方法中被调用。我们来看一下,新添加的这个AbstractLifeCycle会执行什么,执行的就是将Jetty Server的Connectors清空。为什么要做这一步呢?因为如果不将Connectors清空的话,执行Jetty Server的start()方法会将IO端口进行榜单、监听,而此时application context还没有准备就绪。
  3. 执行Jetty Server的start方法,此处会将ServletContext进行一些初始化。
  4. 设置StopAtShutdown标志位为false。

至此,Jetty Server除了Connector之外,其余都已经准备就绪。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void initialize() {
synchronized (this.monitor) {
try {
// Cache the connectors and then remove them to prevent requests being
// handled before the application context is ready.
this.connectors = this.server.getConnectors();
this.server.addBean(new AbstractLifeCycle() {
@Override
protected void doStart() throws Exception {
for (Connector connector : JettyWebServer.this.connectors) {
Assert.state(connector.isStopped(), () -> "Connector "
+ connector + " has been started prematurely");
}
JettyWebServer.this.server.setConnectors(null);
}
});
// Start the server so that the ServletContext is available
this.server.start();
this.server.setStopAtShutdown(false);
}
catch (Exception ex) {...}
}
}

启动

Jetty Server的启动主要是通过start()方法。重点看一下方法的内部实现。

首先,会看到上次缓存的Connectors重新赋值给Jetty Server。然后,会再次调用server的start方法以及初始化handlers。最后,会看到调用Connector的start方法,开启端口监听。

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
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
this.server.setConnectors(this.connectors);
if (!this.autoStart) {
return;
}
try {
this.server.start();
for (Handler handler : this.server.getHandlers()) {
handleDeferredInitialize(handler);
}
Connector[] connectors = this.server.getConnectors();
for (Connector connector : connectors) {
try {
connector.start();
}
catch (BindException ex) {......}
this.started = true;

}
}
catch (Exception ex) {......}
}
}

停止

Jetty Server的停止比较简单,只需要调用其stop方法即可。

1
2
3
4
5
6
7
8
9
public void stop() {
synchronized (this.monitor) {
this.started = false;
try {
this.server.stop();
}
catch (Exception ex) {....}
}
}