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 tProcessorQ//导入 ServletwebServerFactoryConfiguration 的三个内部类, 用来判断应用服务器类型@Import({ ServletWebServerFactoryAutoConfiguration . BeanPostProcessorsRegistrar.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 = SearchStrategy . CURRENT)public static class EmbeddedTomcat {@Beanpublic TomcatServletWebServerF actory tomcatServletWebServerFactory(ObjectProvider connectorCustomizers,ObjectProvider contextCustomizers,ObjectProvider> protocolHandlerCustomizers) {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;}}
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 tomizerBean-PostProcessorpublic static class BeanPostProces sorsRegistrar implements ImportBeanDefinitionRe-gistrar, BeanF actoryAwareprivate ConfigurablelistableBeanFactory beanF actory;//实现 BeanFactoryAware 的方法,设 置 BeanFactory@Overridepublic void setBeanFactory(BeanF actory beanFactory) throws BeansException {if (beanFactory instanceof Conf igurableListableBeanFactory) {this . beanFactory = (ConfigurableListableBeanFactory) beanFactory;//注册一^WebServerFac toryCus tomizerBeanPostProcessor@Overridepublic void registerBeanDefinitions (Annotat ionMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this. beanFactory == null) {return;}registerSyntheticBeanIfMissing(registry, " 'webServerFactoryCustomizer-BeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor .class);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPost -Processor",ErrorPageRegistrarBeanPostProcessor . class);//检查卉注册 Beanprivate void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, Stringname, Class> beanClass) {//检查指定类型的 Bean name 数组是否存在,如果不存在则创建 Bean 并注入容器中if (ObjectUtils . isEmpty(this . beanFactory . getBeanNamesForType(beanClass,true,false))) {RootBeanDefinition beanDefinition = new RootBeanDefinition (beanClass);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@Beanpublic 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 tomcatServletWebServerFactorCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);//实例化注册 FilterRegistrat ionBean//并设置其 DispatcherType 类型和优先级@Bean@ConditionalOnMissingFilterBean(ForwardedHeaderFilter .class)@ConditionalOnProperty(value = "server . forward- headers -strategy", havingValue = "framework")public FilterRegistrationBean< ForwardedHeaderFilter> forwardedHeaderFilter() {ForwardedHeaderFilter filter = new ForwardedHeaderFilter();FilterRegistrationBean registration = new FilterRegistrationBean<>(filter);registration. setDispatcherTypes (Di spatcherType . REQUEST, DispatcherType.ASYNC, DispatcherType . ERROR);registration. setOrder(Ordered.HIGHEST_ PRECEDENCE);return registration;)}}}
前两个方法实例化了两个定制化对象,其中 ServletWebServerFactoryCustomizer 用来配置ServletWeb 服 务 器 的 基 本 信 息 , 比 如 通 常 在 application.properties 中 配 置 的server.port=8080,就会通过 ServerProperties 传递进来进行设置。
我们看一下 ServletWebServerFactoryCustomizer 的核心代码实现。
public class ServletWebServerFactoryCustomizerimplements WebServerFactoryCus tomizer, Ordered {@Overridepublic 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(factory: :setContextPath); map. from(this. serverProperties . getServlet(): :getApplicat ionDisplayName).to(factory: :setDisplayName);}}
通过以上代码我们可以看到,这里将 ServerProperties 中的参数设置到 Property-Mapper中,包括常见的端口、地址、ContextPath、 发布名称等。
而TomcatServletWeb-ServerFactoryCustomizer 的功能也是对ServerProperties中配置的参数进行定制化设置,比如 ContextRoot 设置等。
最后一个方法是 FilterRegistrationBean类的实例化操作。实例化对 应 的 Bean 之 后 , 设置 其 DispatcherType 类 型 和 优先 级 为 最 高 。本 质 上 来 讲 ,Filter-RegistrationBean 是一 个 ServletContextlnitializer ,它的作用是在 Servlet3.0+容器中注册一一个 Filter。很显然,这里是注册了 ForwardedHeaderFilter,用于重定向功能。
至此,Servlet Web 容器的自动配置便完成了。你可能会问,怎么没看到 WebServer 的初始化呢?这正是我们下一节 要讲的内容。
本文给大家讲解的内容是SpringBoot内置Servlet容器源码解析
- 下篇文章给大家讲解的是WebServer初始化过程;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!!!