抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringBoot源码分析-内嵌Tomcat

嵌入式Tomcat简介

​ SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后,tomcat容器、SpringMVC、spring事务等第三方依赖也已经自动启动。那么SpringBoot是如何启动这些第三方依赖的呢?下面以SpringBoot拉起tomcat容器为例,进行源码分析。

​ 我们知道,在使用Spring boot搭建web工程时,我们不需要自己搭建一个tomcat服务器,只需要引入spring-boot-starter-web,在应用启动时会自动启动嵌入版的tomcat作为应用服务器,下面我们来分析这个过程。

Tomcat自动配置

首先是进行SpringBootApplication注解的解析然后是EnableAutoConfiguration注解的解析

导入类AutoConfigurationImportSelector,进行自动配置类加载注册,加载EnableAutoConfiguration类型的注解,其中有一个是关于Tomcat的自动配置类ServletWebServerFactoryAutoConfiguration

​ 进入该类,里面也通过@Import注解将EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow等嵌入式容器类加载进来了,springboot默认是启动嵌入式tomcat容器,如果要改变启动jetty或者undertow容器,需在pom文件中去设置。

​ 之前我们分析了Spring boot的启动流程和自动配置流程,而嵌入tomcat的启动同样需要依赖一个配置ServletWebServerFactoryAutoConfiguration,我们来看一下这个配置的内容:

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
66
67
68
69
70
71
72
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}

@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}

@Bean
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}

/**
* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
* {@link ImportBeanDefinitionRegistrar} for early registration.
*/
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

private ConfigurableListableBeanFactory beanFactory;

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}

private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}

}

}

配置中主要做这么几件事,导入了内部类BeanPostProcessorsRegistrar,它实现了ImportBeanDefinitionRegistrar,作用我们在之前的文章中已经介绍过,我们可以实现ImportBeanDefinitionRegistrar来注册额外的BeanDefinition。导入了ServletWebServerFactoryConfiguration.EmbeddedTomcat配置(我们主要关注tomcat相关的配置)。注册了ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer两个WebServerFactoryCustomizer类型的bean。

注册BeanPostProcessors

我们来分别看一下上面提到的这些类的作用,首先来看内部类BeanPostProcessorsRegistrar:

1
2
3
4
5
6
7
8
9
10
11
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}

这里注册了两个BeanDefinition,WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor,它们都实现了BeanPostProcessor,后面我们会介绍它们的作用。

注册嵌入式Tomcat

这个配置很简单,就是注册了一个TomcatServletWebServerFactory类型的bean。

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
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {

@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
//创建TomcatServletWebServerFactory
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}

}

.......

}

最后剩下两个WebServerFactoryCustomizer类型的bean,任何WebServerFactoryCustomizer类型的bean都可以收到WebServerFactory的回调,所以我们可以实现WebServerFactoryCustomizer来定义WebServerFactory。ServletWebServerFactoryCustomizer的作用是将ServerProperties的属性配置设置到ConfigurableServletWebServerFactory的对应属性上,这里主要设置的是一些通用的属性,上面注册的TomcatServletWebServerFactory就实现了ConfigurableServletWebServerFactory。而TomcatServletWebServerFactoryCustomizer则是为TomcatServletWebServerFactory设置了一些它需要的特有属性。

创建Tomcat

当自动装配功能完成之后会接着执行onRefresh的方法

执行流程是 run->refreshContext->refresh->AbstractApplicationContext#refresh->ServletWebServerApplicationContext#onRefresh

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void onRefresh() {
//创建主题对象,不用在意
super.onRefresh();
try {
//开始创建web服务
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

创建WEB服务

创建web服务,默认获取的是tomcat的web容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//获取servletWebServerFactory,从上下文注册bean中可以找到
ServletWebServerFactory factory = getWebServerFactory();
// 主要看这个方法
//获取servletContextInitializer,获取webServer
this.webServer = factory.getWebServer(getSelfInitializer());
} else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
} catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
//替换servlet相关的属性资源
initPropertySources();
}
获取servletWebServerFactory

因为我们上文在Tomcat自动配置中默认注册了TomcatServletWebServerFactory,所以这个地方就会从BeanFactory中获取到TomcatServletWebServerFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
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);
}

Tomcat对象的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
//使用给定的完全加载的servletContext准备WebApplicationContext
prepareWebApplicationContext(servletContext);
//注册Application作用域(Scop)
registerApplicationScope(servletContext);
//使用给定的BeanFactory注册特定于web的作用域bean(contextParameters,contextAttributes)
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}

完成内嵌Tomcat的api调用

factory.getWebServer(getSelfInitializer());就会调用TomcatServletWebServerFactory的getWebServer方法完成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
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//完成tomcat的api调用
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
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);
}
//准备tomcatEmbeddedContext并设置到tomcat中
prepareContext(tomcat.getHost(), initializers);
//构建tomcatWebServer
return getTomcatWebServer(tomcat);
}

获取tomcat服务

1
2
3
4
5
6
7
8
9
10
11
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
//初始化Tomcat
initialize();
}

完成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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
//engineName拼接instanceId
addInstanceIdToEngineName();

Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
//删除Connectors,以便再启动服务时不发生协议绑定
removeServiceConnectors();
}
});

// Start the server to trigger initialization listeners
//启动服务触发初始化监听器
this.tomcat.start();

// We can re-throw failure exception directly in the main thread
//在主线程中重新抛出失败异常
rethrowDeferredStartupExceptions();

try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}

// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
//所有的tomcat线程都是守护线程,我们创建一个阻塞非守护线程来避免立即关闭
startDaemonAwaitThread();
}
catch (Exception ex) {
//异常停止tomcat
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
删除Connectors

删除Connectors,以便再启动服务时不发生协议绑定

1
2
3
4
5
6
7
8
9
10
11
private void removeServiceConnectors() {
for (Service service : this.tomcat.getServer().findServices()) {
Connector[] connectors = service.findConnectors().clone();
//将将要移除的conntector放到缓存中暂存
this.serviceConnectors.put(service, connectors);
for (Connector connector : connectors) {
//移除connector
service.removeConnector(connector);
}
}
}

启动Tomcat

除了refresh方法之外,在finishRefresh()方法中也对tomcat做了相关的处理

调用ServletWebServerApplicationContext的finishRefresh方法。

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void finishRefresh() {
//调用父类的finishRefresh方法
super.finishRefresh();
//启动webServer
WebServer webServer = startWebServer();
if (webServer != null) {
//发布webServer初始化完成事件
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}

启动服务

1
2
3
4
5
6
7
8
private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
//启动webserver
webServer.start();
}
return webServer;
}

调用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
28
29
30
31
32
33
34
35
36
37
38
@Override
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
//添加之前移除的connector
addPreviouslyRemovedConnectors();
Connector connector = this.tomcat.getConnector();
if (connector != null && this.autoStart) {
//延迟加载启动
performDeferredLoadOnStartup();
}
//检查connector启动状态是否为失败,失败抛出异常
checkThatConnectorsHaveStarted();
this.started = true;
logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
+ getContextPath() + "'");
}
catch (ConnectorStartFailedException ex) {
//异常停止tomcat
stopSilently();
throw ex;
}
catch (Exception ex) {
if (findBindException(ex) != null) {
throw new PortInUseException(this.tomcat.getConnector().getPort());
}
throw new WebServerException("Unable to start embedded Tomcat server", ex);
}
finally {
Context context = findContext();
//context解绑classload
ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
}
}

addPreviouslyRemovedConnectors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void addPreviouslyRemovedConnectors() {
Service[] services = this.tomcat.getServer().findServices();
for (Service service : services) {
//从上面移除connector添加的缓存中取出connector
Connector[] connectors = this.serviceConnectors.get(service);
if (connectors != null) {
for (Connector connector : connectors) {
//添加到tomcat service中
service.addConnector(connector);
if (!this.autoStart) {
//如果不是自动启动,则暂停connector
stopProtocolHandler(connector);
}
}
//添加完成后移除
this.serviceConnectors.remove(service);
}
}
}

performDeferredLoadOnStartup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void performDeferredLoadOnStartup() {
try {
for (Container child : this.tomcat.getHost().findChildren()) {
if (child instanceof TomcatEmbeddedContext) {
//延迟加载启动
((TomcatEmbeddedContext) child).deferredLoadOnStartup();
}
}
}
catch (Exception ex) {
if (ex instanceof WebServerException) {
throw (WebServerException) ex;
}
throw new WebServerException("Unable to start embedded Tomcat connectors", ex);
}
}

deferredLoadOnStartup

1
2
3
4
void deferredLoadOnStartup() throws LifecycleException {
doWithThreadContextClassLoader(getLoader().getClassLoader(),
() -> getLoadOnStartupWrappers(findChildren()).forEach(this::load));
}

关闭Tomcat

在refreshContext中注册一个关闭的钩子函数,而钩子函数可以完成关闭的功能

当服务关闭就会触发ServletWebServerApplicationContext的onClose方法

1
2
3
4
5
@Override
protected void onClose() {
super.onClose();
stopAndReleaseWebServer();
}

调用stopAndReleaseWebServer方法关闭Tomcat

1
2
3
4
5
6
7
8
9
10
11
private void stopAndReleaseWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
try {
webServer.stop();
this.webServer = null;
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}

调用webServer.stop()关闭服务

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
@Override
public void stop() throws WebServerException {
synchronized (this.monitor) {
boolean wasStarted = this.started;
try {
this.started = false;
try {
// 停止tomcat
stopTomcat();
// 销毁服务,调用后对象不能再次使用
this.tomcat.destroy();
}
catch (LifecycleException ex) {
// swallow and continue
}
}
catch (Exception ex) {
throw new WebServerException("Unable to stop embedded Tomcat", ex);
}
finally {
if (wasStarted) {
containerCounter.decrementAndGet();
}
}
}
}

到这里,整个Spring boot内嵌tomcat的流程就分析完了。

评论