上一篇分析到SpringBoot中web嵌入式容器的配置方式和原理。今天我们分析下嵌入式web容器的启动原理。
一、什么时候创建嵌入式的Servlet容器工厂?
-
如果看了上一篇的分析,则对
EmbeddedWebServerFactoryCustomizerAutoConfiguration
这个类有一点了解**(注意:SpringBoot2.0.x以下不是这个类,对应的类为EmbeddedServletContainerAutoConfiguration
)**, 这个类会在SpringBoot启动时,根据当前环境的jar包,来判断具体生成哪个WebServerFactoryCustomizer
,比如tomcat、jetty等。因为默认的是tomcat,所以我们在返回TomcatWebServerFactoryCustomizer
处下断点。然后以调试模式运行application。下断点处源码如下:1
2
3
4
5
6
7
8.class, UpgradeProtocol.class }) ({ Tomcat
public static class TomcatWebServerFactoryCustomizerConfiguration {
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties); //在此处下断点
}
} -
断下来后,调用堆栈如下:
接下来拿关键代码来分析下。
-
SpringApplication
类下的run
方法。(…代表忽略无关代码)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
47public ConfigurableApplicationContext run(String... args) {
....
try {
....
//创建上下文环境 有关代码在下面给出
context = createApplicationContext();
....
//更新Context。在这个方法里创建web容器并启动
refreshContext(context);
....
}
catch (Throwable ex) {
....
}
....
return context;
}
//创建上下文环境
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
//根据应用类型来判断创建哪种上下文环境
switch (this.webApplicationType) {
case SERVLET:
//如果是web型应用,则创建AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
//如果是reactive类型,则创建AnnotationConfigReactiveWebServerApplicationContext
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
//默认创建AnnotationConfigApplicationContext
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
} -
一路ctrl+左键,点击
refreshContext()
来到AbstractApplicationContext
的refreshContext
方法,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
....
try {
....
//初始化容器
onRefresh();
//注册监听器
registerListeners();
//初始化我们自己写的类,并加入到容器
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}catch (BeansException ex) {
...
}finally {
...
}
}
}因为
onRefresh
方法是一个抽象方法,所以我们需要找他的实现类ServletWebServerApplicationContext
-
ServletWebServerApplicationContext
中的onRefresh
方法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
protected void onRefresh() {
super.onRefresh();
try {
//创建web容器,方法实现如下
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
//创建web容器
private void createWebServer() {
WebServer webServer = this.webServer;
//获取Servlet上下文环境
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//获取WebServerFactory,调用这个方法就是去创建WebServerFactory,当创建webServerFactory时,就会触发WebServerFactoryCustomizerBeanPostProcessor这个后置处理器。获取我们的定制器,初始化配置。具体分析在下面
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();
} -
WebServerFactoryCustomizerBeanPostProcessor
这个后置处理器,在上一篇已经分析过了。在这里再次分析下。首先看代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
if (bean instanceof WebServerFactory) {
//如果bean是WebServerFactory类型,就会去调用postProcessBeforeInitialization方法,刚好,我们创建的bean就是这个类型
//postProcessBeforeInitialization()方法如下
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
//postProcessBeforeInitialization方法
"unchecked") (
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
//因为是使用lambda表达式,而我又不怎么熟悉。所以只能说个大概。如有错误,希望大家能够批评指正。
//大致意识是,通过getCustomizers()获取所有的定制器,即会拿到TomcatWebServerFactoryCustomizer。然后调用他们的customize()进行属性设置。
LambdaSafe
.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}到此,容器已经创建,接下来我们看如何启动的。
二、嵌入式web容器在什么时候创建并启动?
上面已经分析了容器工厂的创建,下面说说是怎样通过容器工厂创建容器并启动的。接看上面的第5点中的createWebServer()
方法,源码如下
1 | //创建web容器 |
三、总结
- web应用启动时,调用
SpringApplication
的run
方法。 - 在run方法中,首先会根据应用类型创建对应的上下文环境,之后再调用
refreshContext()
更新上下文环境。 - 来到
AbstractApplicationContext
的refreshContext()
首先调用onRefresh
初始化容器(在此之前还有其他的初始化调用,我们就不说了),然后再初始化注册监听器,然后再是把我们定义的类加入IOC容器中。 - 因为是web类型应用,所以我们会调用
ServletWebServerApplicationContext
的onRefresh()
- 在
onRefresh
中调用createWebServer()
先获取对应的ServletWebServerFactory
,并通过factory.getWebServer()
创建容器,并启动。 - 在获取对应的
ServletWebServerFactory
时会触发``WebServerFactoryCustomizerBeanPostProcessor - 在调用
getWebServer()
时, 创建tomcat容器,再调用getTomcatWebServer()
启动容器。 - 最终启动容器是创建
TomcatWebServer
这个类,并调用其初始化方法initialize()
启动服务器。 - 初始化监听器。
- 把我们自定义的类加入到IOC容器中。