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

1496 篇文章 10 订阅
1494 篇文章 14 订阅

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 服务的。先看其注册部分的源代码。

@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 内部类的代码如下。

@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 的默认值,代码如下。

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。很显然,它是当前自动配置类的内部类,源代码如下。

//通过实现 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 注 解部分以及涉及的类讲解完毕。下面我们再看看该自动配置类内部的其他代码。

// 初始化 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 的核心代码实现。

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. 感谢大家的支持!!!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值