前言
本文是对上篇博文的继续,会以Jetty为例详细讲解一下。
在spring boot中嵌入Jetty最核心的三个类是ServletWebServerApplicationContext
、JettyServletWebServerFactory
和JettyWebServer
。上文中我们讲到ServletWebServerApplicationContext
会在自己的生命周期中绑定create、start以及stop WebServer等操作。
首先我们来看一下在ServletWebServerApplicationContext
中是如何选择ServletWebServerFactory
及WebServer
类型的。
选择ServletWebServerFactory及WebServer
本文主要讲述在ServletWebServerApplicationContext
的createServer
这一步是如何选择以及创建Jetty webServer的。详细可以看一下方法createWebServer()
。分两步:
1)确定ServletWebServerFactory;2)由确定ServletWebServerFactory实例化webServer。
1 | private void createWebServer() { |
选择ServletWebServerFactory
通过调用getWebServerFactory()
方法获取到JettyServletWebServerFactory
或TomcatServletWebServerFactory
等。获取的方式是调用getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class)
,是从context里边的beanFactory中获取类型为ServletWebServerFactory
的bean,并且还需要保证只能有一个该类型的Bean。
1 | protected ServletWebServerFactory getWebServerFactory() { |
那么接下来的问题就是:这个类型为ServletWebServerFactory
的bean是何时注册到beanFactory中的呢?
核心就在于两个类ServletWebServerFactoryConfiguration
和ConfigurationClassPostProcessor
。前者是一个典型的spring configuration类,带有@Configuration
的注解,用于对Servlet WebServer配置。后者是一个BeanFactoryPostProcessor,主要用于启动阶段对@Configuration
的类进行处理。
ServletWebServerFactoryConfiguration类
类ServletWebServerFactoryConfiguration
来自于jar包spring-boot-autoconfigure.查看类的源码可以看到提供了三个webServer的配置Tomcat、Jetty和Undertow。对应有三个加有@Configuration
注解的类EmbeddedTomcat
、EmbeddedJetty
、EmbeddedUndertow
。
1 | class ServletWebServerFactoryConfiguration { |
在实际中,只会有且仅有一个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方法分析
下面是JettyServletWebServerFactory
的getWebServer
方法的源码,可以看到创建Jetty Server的整体流程。
创建 Jetty WebAppContext
创建 Jetty Server
将 ServletContextInitializer 配置到Jetty WebAppContext,后续Jetty WebAppContext初始化时会调用这些ServletContextInitializer.
将Jetty WebAppContext 封装配置成 Jetty Server的Handler
个性化的配置处理
New JettyWebServer,封装上边创建好的Jetty Server。
1 | public WebServer getWebServer(ServletContextInitializer... initializers) { |
上边这一步,我们可以看到Jetty核心三组件Server、Handler(及其封装的AppContext)、Connector(在第2步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
注入到servletContext
的ROOT_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 | public JettyWebServer(Server server, boolean autoStart) { |
重点来看一下方法initialize()
,逐行代码分析一下。
- 首先将Jetty Server的connectors赋值给this.connectors。为什么这么做呢?是因为后边会将Jetty Server的connectors清空,在此处先把这些cache起来,不然一会清空了就找不回来了。
- 其次调用Jetty Server的
addBean()
方法,给Jetty添加一个AbstractLifeCycle的Bean,熟悉Jetty源码的会知道这个Bean的doStart
方法会在Jetty Server的start
方法中被调用。我们来看一下,新添加的这个AbstractLifeCycle会执行什么,执行的就是将Jetty Server的Connectors清空。为什么要做这一步呢?因为如果不将Connectors清空的话,执行Jetty Server的start()
方法会将IO端口进行榜单、监听,而此时application context还没有准备就绪。 - 执行Jetty Server的
start
方法,此处会将ServletContext进行一些初始化。 - 设置StopAtShutdown标志位为false。
至此,Jetty Server除了Connector之外,其余都已经准备就绪。
1 | private void initialize() { |
启动
Jetty Server的启动主要是通过start()
方法。重点看一下方法的内部实现。
首先,会看到上次缓存的Connectors重新赋值给Jetty Server。然后,会再次调用server的start方法以及初始化handlers。最后,会看到调用Connector的start方法,开启端口监听。
1 | public void start() throws WebServerException { |
停止
Jetty Server的停止比较简单,只需要调用其stop方法即可。
1 | public void stop() { |