- 0 嵌入式Servlet容器自动配置原理以及启动原理的步骤
- 1 嵌入式Servlet容器自动配置原理(以Tomcat为例)
- 1.1 简单介绍
- 2 嵌入式Servlet容器启动原理(以Tomcat为例)
- 2.1 SpringBoot启动时,启动Tomcat的原理
- 2.2 嵌入式的Servlet容器工厂创建tomcat容器,初始化并启动容器
- 2.2.1 嵌入式Servlet容器工厂的自动配置类
- 1> 注册后置处理器(WebServerFactoryCustomizerBeanPostProcessor)
- 2> 后置处理器(WebServerFactoryCustomizerBeanPostProcessor)
- 3> web服务工厂定制器(WebServerFactoryCustomizer)
- 2.2.2 嵌入式的Servlet容器工厂的配置类
- 2.2.1 嵌入式Servlet容器工厂的自动配置类
版本说明:
- Spring Boot 2.x 版本:嵌入式Servlet容器自动配置是通过
定制器 来定制的;WebServerFactoryCustomizer
- Spring Boot 1.x 版本:是通过
嵌入式的Servlet容器定制器来定制的。EmbeddedServletContainerCustomizer
SpringBoot官方文档:
Embedded Web Servers、
Embedded Servlet Container Support
参考博客:https://cloud.tencent.com/developer/article/1551867
0 嵌入式Servlet容器自动配置原理以及启动原理的步骤
步骤:
- SpringBoot 启动时,根据导入的依赖信息,自动创建对应的
(web服务工厂定制器);WebServerFactoryCustomizer
-
(web服务工厂定制器组件的后置处理器)获取所有类型为web服务工厂定制器的组件(包含实现WebServerFactoryCustomizer接口,自定义的定制器组件),依次调用WebServerFactoryCustomizerBeanPostProcessor
方法,定制Servlet容器配置;customize
- 嵌入式的Servlet容器工厂创建Tomcat容器,初始化并启动容器。
1 嵌入式Servlet容器自动配置原理(以Tomcat为例)
1.1 简单介绍
SpringBoot默认使用
Tomcat
作为嵌入式的Servlet容器,
这里所谓的嵌入式服务器就是,SpringBoot使用 apache组织 提供的Tomcat的API来配置和定制Tomcat服务。
SpringBoot在启应用动时都会加载各个jar包下的
/META-INF/spring.factories
文件,读取其中的
org.springframework.boot.autoconfigure.EnableAutoConfiguration
类型的所有自动配置类,从而达到自动配置的效果。
而我们的servlet容器的自动配置也是从这个配置文件着手,在
spring-boot-autoconfigure-2.3.1.RELEASE.jar
包下的
/META-INF/spring.factories
配置文件中,有一个自动配置类,是用于Servlet容器的自动配置的。
// ServletWebServerFactoryAutoConfiguration 就是嵌入式servlet容器的自动配置类
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
ServletWebServerFactoryAutoConfiguration
是嵌入式Servlet容器的自动配置类,这个类的主要作用是
创建
TomcatServletWebServerFactory
工厂类,
创建
TomcatServletWebServerFactoryCustomizer
定制器类,
创建
FilterRegistrationBean
注册
ForwardedHeaderFilter
(ForwardedHeaderFilter会从Forwarded,X-Forwarded-Host,X-Forwarded-Port或者X-Forwarded-Proto中获取跳转信息),
同时很关键的一步是注册后置处理器
webServerFactoryCustomizerBeanPostProcessor
,
只要
Application
类一启动,就会执行
run
方法,
run经过一系列调用会通过
ServletWebServerApplicationContext
的
onRefresh
方法创建ioc容器,
然后通过
createWebServer
方法,createWebServer方法会去ioc容器里扫描是否有对应的
ServletWebServerFactory
工厂类(
TomcatServletWebServerFactory
是其中一种),
扫描得到,就会触发
webServerFactoryCustomizerBeanPostProcessor
后置处理器类,
这个处理器类会获取
TomcatServletWebServerFactoryCustomizer
定制器,并调用
customize
方法进行定制,
这时候工厂类起作用,调用
getWebServer
方法进行Tomcat属性配置和引擎设置等等,
再创建TomcatWebServer启动Tomcat容器。
2 嵌入式Servlet容器启动原理(以Tomcat为例)
2.1 SpringBoot启动时,启动Tomcat的原理
主要 是 围绕 ServletWebServerApplicationContext
类进行
(1)我们启动springboot应用时,都是直接运行主程序的
main
方法,然后调用里面的
SpringApplication
类的
run
方法。
(2)调用run方法时,会调用该类自己的
refreshContext
方法,这个方法的作用是创建IOC容器对象,并且初始化IOC容器,创建容器中的每一个组件。
(3)在该类的
reflesh
方法中,再调用了
ServletWebServerApplicationContext
类的
reflesh
方法,刷新刚才的IOC容器后(这个方法其实是继承
AbstractApplicationContext
类的),在该方法中又调用
onreflesh
方法(这个方法则是
ServletWebServerApplicationContext
类重写
AbstractApplicationContext
类的),最后在
onreflesh
中调用
createWebServer
方法,创建 WebServer对象。
(4)
createWebServer
方法,该方法最终能够获取到一个与当前应用(根据我们导入的依赖来获取)所导入的Servlet类型相匹配的web服务工厂
TomcatServletWebServerFactory
,通过该工厂就可以获取到相应的
WebServerFactoryCustomizer
(Web服务工厂定制器),在调用该工厂的
getWebServer
方法,创建->初始化->启动Tomcat。
注:
createWebServer
方法执行期间,我们其实会来到
EmbeddedWebServerFactoryCustomizerAutoConfiguration
,然后根据条件(配置的依赖)配置哪一个Web服务器。
* 在SpringBoot的run启动时,会判断当前所处环境。
* 如果是Web环境则通过创建一个`ServletWebServerApplicationContext`,执行构造函数的refresh方法,
* 在refresh方法内执行onRefresh方法,执行createWebServer方法,这个方法会根据当前应用是内置还是外置发布方式来决定以何种方式获取web服务器。
* 如果是内置方式则通过`TomcatServletWebServerFactory`工厂类来获取一个首选的web服务器,然后进行服务器的初始化配置,应用加载生效以及服务器启动的操作。
2.2 嵌入式的Servlet容器工厂创建tomcat容器,初始化并启动容器
2.2.1 嵌入式Servlet容器工厂的自动配置类
ServletWebServerFactoryAutoConfiguration
// 标注这是一个配置类
@Configuration(
proxyBeanMethods = false
)
// 决定配置类的加载顺序的,当注解里的值越小越先加载
@AutoConfigureOrder(-2147483648)
// 表示当前必须有 servlet-api 依赖存在
@ConditionalOnClass({ServletRequest.class})
// 仅仅基于Servlet的web应用程序
@ConditionalOnWebApplication(
type = Type.SERVLET
)
// 向IOC容器中添加ServerProperties组件
@EnableConfigurationProperties({ServerProperties.class})
// 向IOC容器中注入一些组件
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
EmbeddedTomcat.class,
EmbeddedJetty.class,
EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
//略
}
从
@Import
注解可以看到,这个配置类向容器中批量添加组件,支持添加三种web服务器(Tomcat、Jetty、Undertow),而具体要配置哪个服务器,由
ServletWebServerFactoryConfiguration
决定,同时还定义了配置的顺序Tomcat>Jetty>Undertow,意思就是当环境中有Tomcat满足的依赖时就会优先使用Tomcat,依次往后推。
综合来看,
ServletWebServerFactoryConfiguration
自动配置类作了以下几件事:
- 向IOC容器中添加了内部类
这个bean后置处理器注册器组件,这个组件也是向IOC容器中添加组件后置处理器,这里添加的是BeanPostProcessorsRegistrar
服务器工厂的定制器的后置处理器 ,对于WebServerFactoryCustomizerBeanPostProcessor
来说,都是bean的后置处理器,即在bean创建完成后,在其初始化前后进行拦截处理。注册的后置处理器类xxxBeanPostProcessor
,在ioc容器启动的时候会调用。WebServerFactoryCustomizerBeanPostProcessor
- 向IOC容器中添加了
等嵌入容器相关配置(这里以 tomcat 相关的配置为例)。ServletWebServerFactoryConfiguration.EmbeddedTomcat
- 向IOC容器中添加了
、ServletWebServerFactoryCustomizer
两个TomcatServletWebServerFactoryCustomizer
类型的 bean。WebServerFactoryCustomizer
1> 注册后置处理器(BeanPostProcessorsRegistrar)
部分代码
// ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
// 略
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (this.beanFactory != null) {
// 注册 webServerFactoryCustomizerBeanPostProcessor
this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
// 注册 errorPageRegistrarBeanPostProcessor
this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
}
}
// 略
}
BeanPostProcessorsRegistar
注册了两个 bean,一个
WebServerFactoryCustomizerBeanPostProcessor
,一个
errorPageRegistrarBeanPostProcessor
;这两个都实现类
BeanPostProcessor
接口,属于 bean 的后置处理器,作用是在 bean 初始化前后加一些自己的逻辑处理。
-
:作用是在WebServerFactoryCustomizerBeanPostProcessor
初始化时调用上面自动配置类注入的那些WebServerFactory
,然后调用 WebServerFactoryCustomizer 中的WebServerFactoryCustomizer
方法来 处理 WebServerFactory。customize
- errorPageRegistrarBeanPostProcessor:和上面的作用差不多,不过这个是处理 ErrorPageRegistrar 的。
2> 后置处理器(WebServerFactoryCustomizerBeanPostProcessor)
这个后置处理器,拿到所有WebServerFactoryCustomizer,分别执行对应的customize方法,定制Servlet容器,自定义的WebServerFactoryCustomizer是最后执行的,所以可以覆盖之前的默认的配置,最后 绑定到 工厂类(WebServerFactory)。
部分代码
// org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
// 初始化 bean 之前调用
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 判断这个bean 是不是 继承自 WebServerFactory
if (bean instanceof WebServerFactory) {
// 是则执行
this.postProcessBeforeInitialization((WebServerFactory)bean);
}
return bean;
}
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
// 遍历WebServerFactory 中所有 WebServerFactoryCustomizer 类,执行对应的customize方法,定制Servlet容器
((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
customizer.customize(webServerFactory);
});
}
// 初始化 bean 之后调用
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
3> web服务工厂定制器(WebServerFactoryCustomizer)
以和
TomcatWebServerFactoryCustomizer
ServletWebServerFactoryCustomizer
为例
这两个 Customizer 实际上就是去处理一些配置值,然后绑定到 各自的工厂类中,将
配置值绑定给
ServerProperties
对象实例上
ConfigurableServletWebServerFactory
TomcatWebServerFactoryCustomizer
部分代码
public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
// 略
@Override
public void customize(TomcatServletWebServerFactory factory) {
// 拿到 tomcat 相关的配置
ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
// server.tomcat.additional-tld-skip-patterns
if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
factory.getTldSkipPatterns()
.addAll(tomcatProperties.getAdditionalTldSkipPatterns());
}
// server.redirectContextRoot
if (tomcatProperties.getRedirectContextRoot() != null) {
customizeRedirectContextRoot(factory,
tomcatProperties.getRedirectContextRoot());
}
// server.useRelativeRedirects
if (tomcatProperties.getUseRelativeRedirects() != null) {
customizeUseRelativeRedirects(factory,
tomcatProperties.getUseRelativeRedirects());
}
// 同上,略
}
// 略
}
ServletWebServerFactoryCustomizer
部分代码
public class ServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
// 略
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
// 端口
map.from(this.serverProperties::getPort).to(factory::setPort);
// address
map.from(this.serverProperties::getAddress).to(factory::setAddress);
// contextPath
map.from(this.serverProperties.getServlet()::getContextPath)
.to(factory::setContextPath);
// displayName
map.from(this.serverProperties.getServlet()::getApplicationDisplayName)
.to(factory::setDisplayName);
// session 配置
map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
// ssl
map.from(this.serverProperties::getSsl).to(factory::setSsl);
// jsp
map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
// 压缩配置策略实现
map.from(this.serverProperties::getCompression).to(factory::setCompression);
// http2
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
// serverHeader
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
// contextParameters
map.from(this.serverProperties.getServlet()::getContextParameters)
.to(factory::setInitParameters);
// 同上,略
}
//略
}
2.2.2 嵌入式的Servlet容器工厂的配置类
ServletWebServerFactoryConfiguration
,分别对上述支持的三种servlet容器进行了配置。
@Configuration(
proxyBeanMethods = false
)
class ServletWebServerFactoryConfiguration {
// 略 三个内部类
}
可以发现这个类中有三个内部类,而这三个内部类也是具体的Servlet容器工厂配置类,分别通过
@ConditionalOnClass
及
@ConditionalOnMissingBean
限制配置类是否生效(判断是否存在自己手动添加的ServletWebServerFactory),由此可见,要想切换servlet容器,只需要添加相应容器的依赖,排除其他容器依赖即可。
以嵌入式的Tomcat 服务器为例,判断环境中是否引入了Tomcat所需的依赖
Servlet.class
,
Tomcat.class
,
UpgradeProtocol.class
,同时用户没有自己进行Web服务器配置(比如自己通过实现
ServletWebServerFactory
接口进行手动配置web服务器),这个工厂类内部就会对Tomcat进行一些初始化操作,那么这个Tomcat服务器就会生效。
// 嵌入式的Tomcat配置类
@Configuration(
proxyBeanMethods = false
)
// 判断环境中是否引入了Tomcat所需的依赖`Servlet.class`, `Tomcat.class`, `UpgradeProtocol.class`
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
// 用户没有自己实现`ServletWebServerFactory`接口,配置Web服务,加入IOC容器
@ConditionalOnMissingBean(
value = {ServletWebServerFactory.class},
search = SearchStrategy.CURRENT
)
static class EmbeddedTomcat {
EmbeddedTomcat() {
}
// 通过@Bean向容器中添加了TomcatServletWebServerFactory组件
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
可以知道这是 TomcatServletWebServer 容器工厂,
TomcatServletWebServerFactory
有一个
getWebServer
方法获得TomcatWebServer,对Tomcat进行的一些初始化操作,最重要的操作都在
getWebServer
方法内。
-
方法:getWebServer
- 起初先创建一个tomcat,在设置一些配置,再通过
获得TomcatWebServer。getTomcatWebServer(tomcat)
- 起初先创建一个tomcat,在设置一些配置,再通过
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
// 创建 Tomcat
// 用的是最底层的tomcat实例进行配置(通过new Tomcat的方式,而这个Tomcat是tomcat源码包的一个实例类 package org.apache.catalina.startup)
// 主要对端口,协议,tomcat组件对象等进行初始化并封装
Tomcat tomcat = new Tomcat();
// 以下是对 Tomcat 的一些设置
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
// 将要发布的Web应用信息Context初始化到tomcat中
this.prepareContext(tomcat.getHost(), initializers);
// 获得 TomcatWebServer
return this.getTomcatWebServer(tomcat);
}
进入到
getTomcatWebServer(tomcat)
方法中
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
// 对 tomcat 的初始化,进行封装和启动
// 最后将这个tomcat对象封装为一个TomcatWebServer对象供SpringBoot启动时调用
return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}
进入
TomcatWebServer
类中
// 对 tomcat 的初始化,进行封装和启动
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
// 初始化 Tomcat
this.initialize();
}
// 初始化 tomcat ,并启动
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
synchronized(this.monitor) {
try {
this.addInstanceIdToEngineName();
Context context = this.findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && "start".equals(event.getType())) {
this.removeServiceConnectors();
}
});
// 对初始化好的 tomcat 进行启动
this.tomcat.start();
this.rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
} catch (NamingException var5) {
}
this.startDaemonAwaitThread();
} catch (Exception var6) {
this.stopSilently();
this.destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", var6);
}
}
}
综上所述,Servlet容器的启动原理,实际上是SpringBoot通过创建原生Tomcat对象,对这个对象进行端口,协议,组件等初始化,并且将Web应用信息Context对象封装到这个 tomcat对象中,然后,在Web应用信息配置生命周期监听生效后,启动tomcat,最后将这个过程封装到一个WebServer对象中供SpringBoot启动时调用。