SpringBoot内置Servlet源码解析:容器自动配置

2022-10-28 16:22:18 浏览数 (1)

SpringBoot内置Servlet容器源码解析

我们都知道,在使用 Spring Boot 时可以内嵌 Tomcat 等 Servlet 容器,通过直接执行 jar -jar命令即可启动。那么 Spring Boot 是如何检测到对应的 Servlet 容器,又如何进行自动配置的呢?对于之前自动配置的 DispatcherServlet 又是如何获取并注册的?本章就带大家来学习Spring Boot 集成 Servlet Web 容器及 DispatcherServlet 的加载过程。

Web容器自动配置

Servlet Web 服务器概述

在学习源代码之前,先来看一个结构图, 从整体上了解一下Spring Boot 对Servlet Web的支持 , 以及都包含哪些核心部分, 如图 7-1 所 示 。

图 7-1 中,第一列为 Servlet 容器名称, 表示 Spring Boot 内置支持的 Web 容器类型,目前包括 Tomcat、Jetty、 Undertow。第列为针对不同的 Web 容器的 WebServer 实现类,用于控制 Web 容器的启动和停止等操作。第三列为创建第二列中具体 WebServer 的工厂方法类。

以上 Servlet 容器相关支持类均位于 spring-boot 项目的 org. springframework.boot.web 包下,而以上容器的具体实现位于 org.springframework.boot.web.embedded 下。

以 Tomcat 为例,通过自动配置先初始化 TomcatServletWebServerFactory 工厂类,在Spring Boot 启动过程中,该工厂类会通过其 getWebServer 方法创建 TomcatWebServer实例,启动 Tomcat 等一 系列操作。我们先从整体上有个概念,下面再继续分析具体源码实现。

自动配置源码分析

在 Spring Boot 中,Servlet Web 容器的核心配置就是上面提到的 3 个工厂方法的实例化和BeanPostProcessor 的注册。在讲 DispatcherServletAutoConfiguration 自动配置时,我们并没有详细讲

解 其 中 的 @AutoConfigureAfter 注 解 , 该 注 解 内 指 定 的 类 为 ServletWebServerFactoryAuto-Configuration,即在完成了 Web Server 容器的自动配置之后 , 才 会 进 行 DispatcherServlet 的 自 动 配 置 。而 本 节 要 讲 的 内 容 就 是 从ServletWebServerFactoryAutoConfiguration 开始的。

ServletWebServerFactoryAutoConfiguration 是用来自动配置 Servlet 的 Web 服务的。先看其注册部分的源代码。

代码语言:javascript复制
@Configuration(proxyBeanMethods=false)
@AutoConf igureOrder(Ordered.HIGHEST PRECEDENCE)
//需要存在 ServletRequest 类
@ConditionalOnClass (ServletRequest.class)
//需要 web 类型为 Servlet 类型
@ConditionalOnWebApplication(type = Type . SERVLET)
//加戴 ServerProperties 中的配置
@EnableConfigurat ionProperties (ServerProperties.class)
//导入内部类 BeanPostProcessorsRegistrar 用来注 BeanPos tProcessor
Q
//导入 ServletwebServerFactoryConfiguration 的三个内部类, 用来判断应用服务器类
型
@Import({ ServletWebServerFactoryAutoConfiguration . BeanPostProcessorsRegist
rar.class,
ServletwebServerFactoryConfiguration. EmbeddedTomcat . class,
ServletWebServerFactoryConfiguration. EmbeddedJetty . class,
ServletWebServerFactoryConfiguration. EmbeddedUndertow. class })
public class ServletWebServerFactoryAutoConfiguration {
}

注解中常规的项就不多说了,我们重点看一下@lmport 注解中引入的内容。该注解引入了当前类的内部类 BeanPostProcessorsRegistrar 和 ServletWebServerFactoryConfiguration 的3 个内部类: EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow。

先来看 ServletWebServerFactoryConfiguration 类,它是 Servlet Web 服务器的配置类,目前该类中包含了内置 Tomcat. Jetty 和 Undertow 的配置, 重点作用就是实例化图 7-1 中的工厂类。

ServletWebServerFactoryConfiguration 类中定义的 3 个内部类,-般通过@Import 注解在其 他 自 动 配 置 类 中 引 入 使 用 , 并 确 保 其 执 行 顺 序 。在ServletWebServerFactoryAutoCon-figuration 中的使用便是实例。

ServletWebServerFactoryConfiguration 中具体工厂"Bean 的初始化操作基本相同,都是在方法内通过 new 创建对应的工厂类,设置其初始化参数,然后注入 Spring 容器中。下面我们以其中最常用的 Tomcat 容器为例来进行源码层面的讲解。

EmbeddedTomcat 内部类的代码如下。

代码语言:javascript复制
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet. class, Tomcat. class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = S
earch
Strategy . CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatServletWebServerF actory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCusto
mizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFacto
ry();
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;
}
}

EmbeddedTomcat 自动配置的条件是类路径中存在 Servlet、Tomcat、UpgradeProtocol这 3 个类,并且 ServletWebServerFactory 不存在。

在上述代码中,需要注意@ConditionalOnMissingBean 注解的 search 属性。search 属 性支持的搜索策略类型定义在枚举类 SearchStrategy 中,包括 CURRENT、ANCESTORS、ALL。这 3 种策略类型依次对应的作用范围为:搜索当前容器、搜索所有祖先容器(不包括当前容器)、搜索所有层级容器。

默认情况下,search 属性的值为 ALL,也就是搜索所有层级容器,而此处 Search 属性是CURRENT,即搜索范围是当前容器。

TomcatServletWebServerFactory 的实例化方法 tomcatServletWebServerFactory 是由 3个 ObjectProvider 参数构成。

ObjectProvider 参 数 中 的 泛 型 依 次 包 括 TomcatConnector-Customizer 、

TomcatContextCustomizer 和 TomcatProtocolHandlerCustomizer<?>,它们均为回调接口。

TomcatConnectorCustomizer 用于 Tomcat Connector 的定制化处理,

TomcatContextCustomizer 用于 Tomcat Context 的定制化处理,

TomcatProtocolHandlerCustomizer<?>用于 TomcatConnector 中 ProtocolHandler 的定制化处理。也就是说,通过以上回调函数,可以在核心业务处理完成之后,针对 Tomcat 再进行一些定制化操作。

关于 ObjectProvider 的使用,我们在此稍微拓展一-下,有助于我们加深理解。Object-Provider 接口从 Spring 4.3 版本开始引入,它是 ObjectFactory 的一种变体,是专门为注入设计的。在正常情况下,如果构造方法依赖某个 Bean,则需通过@Autowired 进行注入, 并且在单构造函数时可以默认省略掉@Autowired 隐式注入。

但如果待注入的参数的 Bean 为空或有多个时,便是 ObjectProvider 发挥作用的时候了。如果注入实例为空,使用 ObjectProvider 则避免了强依赖导致的依赖对象不存在;如果有多个实例,ObjectProvider 的方法会 根据 Bean 实现的 Ordered 接口或@Order 注解指定的先后顺序获取一个 Bean,从而提供一个更加宽松的依赖注入方式。Spring 5.1 版本之后提供了基于 Stream 的 orderedStream 方法来获取有序的 Stream,这也正是上面源代码中所使用的方法。

TomcatServletWebServerFactory 的实例化代码非常简单,只是调用了无参的构造方法。该工厂方法的层级比较复杂,我们也没必要详细说明所有的父类或接口,只需要知道该类最终是 WebServerFactory 接口的实现即可。

这里先顺便了解一下 TomcatServletWebServerFactory 中两个常见的 Web 应用的默认值:contextPath 和 port, 即我们通常讲的访问路径和端口。在调用无参构造方法时,这两个参数分别默认定义在 AbstractServletWebServerFactory 和AbstractConfigurableWebServerFactory 类中。

TomcatServletWebServerFactory 的 父 类 AbstractServletWebServerFactory 中 定 义 了context-Path 的默认值,代码如下。

代码语言:javascript复制
public abstract class AbstractServletWebServerFactory extends AbstractConfigura -
bleWebServerFactory implements ConfigurableServletWebServerFactory {
private String contextPath = "";
}
AbstractServletWebServerFactory 的父类 AbstractConfigurableWebServerFactory 中定义
了 port 的默认值,代码如下。
public abstract class AbstractConfigurableWebServerFactory implements Configura-
bleWebServerFactory {
private int port = 8080 ;
。。。
}

当然,还有其他许多默认值,比如编码(UTF-8) 等,内容过于细碎,读者可自行查阅相关源代码进行了解在 ServletWebServerFactoryConfiguration 类中还提供了自动配置JettyServletWebServerFactory 和 UndertowServletWebServerFactory 的 内 部 类 , 与Tomcat 的操作基本一致,不再重复讲解。

现在我们回归最初的主线继续看 ServletWebServerFactoryAutoConfiguration 引入的另外一个类:ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar。很显然,它是当前自动配置类的内部类,源代码如下。

代码语言:javascript复制
//通过实现 ImportBeanDefinitionRegistrar 来注册一 ^WebServerFac toryCus tomizer
Bean-
PostProcessor
public static class BeanPostProces sorsRegistrar implements ImportBeanDefi
nitionRe-
gistrar, BeanF actoryAware
private ConfigurablelistableBeanFactory beanF actory;
//实现 BeanFactoryAware 的方法,设 置 BeanFactory
@Override
public void setBeanFactory(BeanF actory beanFactory) throws BeansExcepti
on {
if (beanFactory instanceof Conf igurableListableBeanFactory) {
this . beanFactory = (ConfigurableListableBeanFactory) beanFactory;
//注册一^WebServerFac toryCus tomizerBeanPostProcessor
@Override
public void registerBeanDefinitions (Annotat ionMetadata importingClassMe
tadata,
BeanDefinitionRegistry registry) {
if (this. beanFactory == null) {
return;}
registerSyntheticBeanIfMissing(registry, " 'webServerFactoryCustomizer-
BeanPostProcessor",
WebServerFactoryCustomizerBeanPostProces
sor .class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPost -
Processor",
ErrorPageRegistrarBeanPostProcessor . clas
s);
//检查卉注册 Bean
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry regist
ry, String
name, Class<?> beanClass) {
//检查指定类型的 Bean name 数组是否存在,如果不存在则创建 Bean 并注入容器中
if (ObjectUtils . isEmpty(this . beanFactory . getBeanNamesForType(beanClass,
true,
false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition (beanClas
s);
beanDefinition. setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}

我们知道 Spring 在注册 Bean 时,大多都使用 importtBeanDefinitionRegistrar 接口来实现而这里 beanPostProcessorsRegistrar 的实现完全可以说是按照 Spring 官方模式来进行Bean 的注册。

一般情况下, 我们首先定义一-个 ImportBeanDefinitionRegistrar 接口的实现类,然后在有@Configuration 注解的配置类上使用@lmport 导入该实现类。

其中,在实现类的 registerBeanDefinitions 方法中实现具体 Bean 的注册功能。

对照 BeanPostProcessorsRegistrar 的使用方法,你会发现它是完全按照此模式进行 Bean动态注册的。

在实现 ImportBeanDefinitionRegistrar 接口的同时,还可以实现 BeanFactoryAware 接口,用来设置用于检查 Bean 是否存在的 BeanFactory。

BeanFactory 的使用体现在 register SyntheticBeanlfMissing 方法中。具体完成 Bean 的实例化,并向容器中注册 Bean 是由 RootBeanDefinition 来完成的。

在 BeanPostProcessorsRegistrar 中注册的两个 Bean 都实现自接口 BeanPostProcessor,属于 Bean 的后置处理,作用是在 Bean 初始化之后添加一些自己的逻辑处理。WebServerFactoryCustomizerBeanPostProcessor 的作用主要是在 WebServerFactory 初始化时获取自动配置类注入的 WebServerFactoryCustomizer,然后分别调用 WebServer-FactoryCustomizer 的 customize 方法来进行 WebServerFactory 的定制处理。

ErrorPageRegist-rarBeanPostProcessor 的作用是搜集容器中的 ErrorPageRegistrar,添加到当前应用所采用的 ErrorPageRegistry 中。

至此, ServletWebServerFactoryAutoConfiguration 注 解部分以及涉及的类讲解完毕。下面我们再看看该自动配置类内部的其他代码。

代码语言:javascript复制
// 初始化 ServletwebServerFactoryCustomizer
@Bean
public ServletWebServerFactoryCus tomizer servletWebServerF actoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerF actoryCustomizer( serverProperties) ;
// 初始化 TomcatServletWebServerFac toryCus tomizer
@Bean
@Conditional0nClass(name = "org . apache. catalina. startup. Tomcat")
public TomcatServletWebServerF actoryCustomizer tomcatServletWebServerFactor
Customizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
//实例化注册 FilterRegistrat ionBean<ForwardedHeaderFilter>
//并设置其 DispatcherType 类型和优先级
@Bean
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter .class)
@ConditionalOnProperty(value = "server . forward- headers -strategy", havingVal
ue = "framework")
public FilterRegistrationBean< ForwardedHeaderFilter> forwardedHeaderFilter
() {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRe
gistrationBean<>(filter);
registration. setDispatcherTypes (Di spatcherType . REQUEST, DispatcherType.AS
YNC, DispatcherType . ERROR);
registration. setOrder(Ordered.HIGHEST_ PRECEDENCE);
return registration;
)
}
}
}

前两个方法实例化了两个定制化对象,其中 ServletWebServerFactoryCustomizer 用来配置ServletWeb 服 务 器 的 基 本 信 息 , 比 如 通 常 在 application.properties 中 配 置 的server.port=8080,就会通过 ServerProperties 传递进来进行设置。

我们看一下 ServletWebServerFactoryCustomizer 的核心代码实现。

代码语言:javascript复制
public class ServletWebServerFactoryCustomizer
implements WebServerFactoryCus tomizer<ConfigurableServletWebServerFac-
tory>, Ordered {
@Override
public void customize (ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper . get(). alwaysApplyingWhenNonNu1l();
map . from(this . serverProperties: :getPort). to(factory::setPort);
map. from(this . serverProperties: :getAddress) . to(factory: :setAddress);
map. from(this. serverProperties . getServlet():: getContextPath) . to(factor
y: :set
Context
Path); map. from(this. serverProperties . getServlet(): :getApplicat ionDisplayName).
to(factory: :setDisplayName);
}
}

通过以上代码我们可以看到,这里将 ServerProperties 中的参数设置到 Property-Mapper中,包括常见的端口、地址、ContextPath、 发布名称等。

而 TomcatServletWeb-ServerFactoryCustomizer 的功能也是对ServerProperties中配置的参数进行定制化设置,比如 ContextRoot 设置等。

最后一个方法是 FilterRegistrationBean<ForwardedHeaderFilter>类的实例化操作。实例化对 应 的 Bean 之 后 , 设置 其 DispatcherType 类 型 和 优先 级 为 最 高 。本 质 上 来 讲 ,Filter-RegistrationBean 是一 个 ServletContextlnitializer ,它的作用是在 Servlet3.0 容器中注册一一个 Filter。很显然,这里是注册了 ForwardedHeaderFilter,用于重定向功能。

至此,Servlet Web 容器的自动配置便完成了。你可能会问,怎么没看到 WebServer 的初始化呢?这正是我们下一节 要讲的内容。

本文给大家讲解的内容是SpringBoot内置Servlet容器源码解析

  1. 下篇文章给大家讲解的是WebServer初始化过程;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!!!

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

0 人点赞