Spring Boot Web 和传统的 Spring Web 应用启动有所不同,以前是通过在 web.xml 配置文件中配置 Servlet 来完成 Spring MVC 的启动,但在 Spring Boot Web 中是通过 DispatcherServletAutoConfiguration 来完成初始化工作的,下面我们就来分析下 Spring Boot Web 的初始化流程。

DispatcherServlet 自动配置

根据自动配置的原理,我们在 spring-boot-autoconfigure 包中的 META-INF/spring.factories 配置文件中可以找到针对 Servlet 自动配置的 EnableAutoConfiguration 配置类:

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

DispatcherServlet 自动配置位于 DispatcherServletAutoConfiguration 类中。首先来看下 DispatcherServletAutoConfiguration 上面的注解。

  1. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  2. @Configuration(proxyBeanMethods = false)
  3. @ConditionalOnWebApplication(type = Type.SERVLET)
  4. @ConditionalOnClass(DispatcherServlet.class)
  5. @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
  6. public class DispatcherServletAutoConfiguration {}
  • @AutoConfigureOrder:指定自动配置加载的顺序
  • @Configuration:指定该类为配置类,交给 Spring 容器管理,默认不使用代理
  • @ConditionalOnWebApplication:表示只有 Web 应用才会加载此类
  • @ConditionalOnClass:表示只有存在 DispatcherServlet 类的情况下才会加载此类
  • @AutoConfigureAfter:ServletWebServerFactoryAutoConfiguration 类加载完成后才会加载此类。

此外,在 DispatcherServletAutoConfiguration 内部主要提供了四个静态内部类,下面来分析下:

1. DispatcherServletConfiguration

首先是内部类 DispatcherServletConfiguration,主要完成了 DispatcherServlet 的初始化。源码如下:

  1. @Configuration(proxyBeanMethods = false)
  2. // 实例化条件,通过该类来判断
  3. @Conditional(DefaultDispatcherServletCondition.class)
  4. @ConditionalOnClass(ServletRegistration.class)
  5. // 加载HttpProperties和WebMvcProperties属性配置
  6. @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
  7. protected static class DispatcherServletConfiguration {
  8. public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
  9. @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  10. public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
  11. // 创建DispatcherServlet
  12. DispatcherServlet dispatcherServlet = new DispatcherServlet();
  13. // 初始化DispatcherServlet各项配置
  14. dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
  15. dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
  16. dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
  17. dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
  18. dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
  19. return dispatcherServlet;
  20. }
  21. // 初始化上传文件解析器
  22. @Bean
  23. @ConditionalOnBean(MultipartResolver.class)
  24. @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
  25. public MultipartResolver multipartResolver(MultipartResolver resolver) {
  26. return resolver;
  27. }
  28. }

通过以上源码可以看出,当满足指定的条件后,会对 DispatcherServletConfiguration 进行实例化,而该类内部通过 @Bean 注解的方法会被实例化成 Bean 并注入 Spring 容器中。其中,在 DispatcherServlet 方法中完成了 DispatcherServlet 的实例化和基本设置,这些参数通过 HttpProperties 和 WebMvcProperties 获得。并且指定 Bean 的 name 为 dispatcherServlet,该名称默认会被映射到根 URL 的 / 访问路径。

DispatcherServletConfiguration 中还定义了上传文件的解析器 MultipartResolver 的 Bean 初始化操作,准确来说是 Bean 名称转化的操作。通过条件注解判断,当 MultipartResolver 的 Bean 存在,但 Bean 的名称不为指定的 “multipartResolver” 时,则将其重命名为 multipartResolver。

再回到 DispatcherServletConfiguration 的注解部分,@Conditional 指定的条件类为另外一个内部类 DefaultDispatcherServletCondition,其判断部分的代码如下:

  1. public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
  2. ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
  3. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  4. // 获取类型为DispatcherServlet的Bean名称列表
  5. List<String> dispatchServletBeans = Arrays
  6. .asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
  7. // 如果Bean名称列表中包含dispatcherServlet,则返回不匹配
  8. if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
  9. return ConditionOutcome.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
  10. }
  11. // 如果beanFactory中包含名称为DispatcherServlet的Bean,则返回不匹配
  12. if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
  13. return ConditionOutcome.noMatch(message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
  14. }
  15. // 如果bean名称列表为空,则返回匹配
  16. if (dispatchServletBeans.isEmpty()) {
  17. return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
  18. }
  19. // 其他情况也返回匹配
  20. return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
  21. .items(Style.QUOTE, dispatchServletBeans).append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
  22. }

DefaultDispatcherServletCondition 的核心流程只做了一件事,就是防止重复生成 DispatcherServlet。

DispatcherServletConfiguration 类中还有一个注解 @EnableConfigurationProperties,该注解指定了两个配置类:HttpProperties 和 WebMvcProperties。这两个配置类正是初始化 DispatcherServlet 时用于初始化的参数值,查看这两个类的源码发现,它们分别对应加载了以 “spring.http” 和 “spring.mvc” 为前缀的配置项,我们可以在 application.properties 文件中进行相应配置。

  1. @ConfigurationProperties(prefix = "spring.http")
  2. @ConfigurationProperties(prefix = "spring.mvc")

2. DispatcherServletRegistrationConfiguration

下面再来看用于注册 DispatcherServlet 的 DispatcherServletRegistrationConfiguration 类。源码如下:

  1. @Configuration(proxyBeanMethods = false)
  2. // 实例化条件,通过该类来判断
  3. @Conditional(DispatcherServletRegistrationCondition.class)
  4. @ConditionalOnClass(ServletRegistration.class)
  5. // 加载WebMvcProperties属性配置
  6. @EnableConfigurationProperties(WebMvcProperties.class)
  7. @Import(DispatcherServletConfiguration.class)
  8. protected static class DispatcherServletRegistrationConfiguration {
  9. @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
  10. @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  11. public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
  12. // 通过DispatcherServletRegistrationBean将dispatcherServlet注册为Servlet,这样servlet才会生效
  13. // 注册的路径为根路径"/"
  14. DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
  15. // 设置名称为dispatcherServlet
  16. registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
  17. // 设置加载优先级,默认为-1
  18. registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
  19. multipartConfig.ifAvailable(registration::setMultipartConfig);
  20. return registration;
  21. }
  22. }

DispatcherServletRegistrationConfiguration 的核心功能就是注册 dispatcherServlet,使其生效并设置一些初始化的参数。其中,DispatcherServletRegistrationBean 继承自 ServletRegistrationBean,主要为
DispatcherServlet 提供服务。DispatcherServletRegistrationBean 和 DispatcherServlet 都提供了注册 Servlet 并公开 DispatcherServletPath 信息的功能。

在构造 DispatcherServletRegistrationBean 时除了成员变量赋值之外,还调用了父类 ServletRegistrationBean 的构造方法和 addUrlMappings 方法,用来注册映射路径。

  1. public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {
  2. super(servlet);
  3. Assert.notNull(path, "Path must not be null");
  4. this.path = path;
  5. // 获取Servlet的URL的匹配
  6. super.addUrlMappings(getServletUrlMapping());
  7. }

我们再回到针对 DispatcherServletRegistrationConfiguration 的注解 @Conditional 指定的限定条件类 DispatcherServletRegistrationCondition,该限定条件类主要用来判断 dispatcherServlet 和 dispatcherServletRegistration 是否存在等。

至此,在该自动配置类中,DispatcherServlet 的创建、简单初始化和注册流程已经完成。当第一次接收到网络请求时,DispatcherServlet 内部会进行一系列的初始化操作,这些更多属于 Spring 的内容,不再详细展开。

Spring MVC 自动配置

在 Spring Boot 中引入了 spring-boot-starter-web 依赖,并完成了 DispatcherServlet 的自动配置之后,便会通过 WebMvcAutoConfiguration 进行 Spring MVC 的自动配置。和 DispatcherServletAutoConfiguration 一样,它也是在 spring-boot-autoconfigure 包中的 META-INF/spring.factories 配置文件中注册的:

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

下面分析下源码,先看 WebMvcAutoConfiguration 的注解部分:

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnWebApplication(type = Type.SERVLET)
  3. @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
  4. @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
  5. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
  6. @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
  7. ValidationAutoConfiguration.class })
  8. public class WebMvcAutoConfiguration {}

WebMvcAutoConfiguration 类的实例化需要满足很多条件,其中就有 DispatcherServletAutoConfiguration 的初始化。Spring MVC 在自动配置中的代码较多,官方文档中重点提到了以下功能的实现:

  • 定义 ContentNegotiatingViewResolver 和 BeanNameViewResolver 的 Bean
  • 对静态资源的支持,包括对 WebJars 的支持
  • 自动注册 Converter、GenericConverter、Formatter 的 Bean
  • 对 HttpMessageConverters 的支持
  • 自动注册 MessageCodeResolver
  • 对静态 index.html 的支持
  • 使用 ConfigurableWebBindingInitializer 的 Bean

当然,在自动配置类中不只包括了以上功能,下面就挑几个代表性功能进行源码及实例化过程的分析:

1. ViewResolver 解析

这里以 ContentNegotiatingViewResolver 和 BeanNameViewResolver 的 bean 的实例化为例进行相应解析。其中,ContentNegotiatingViewResolver 实例化相关源码如下:

  1. @Bean
  2. @ConditionalOnBean(ViewResolver.class)
  3. @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
  4. public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
  5. ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
  6. resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
  7. resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
  8. return resolver;
  9. }

ContentNegotiatingViewResolver 实例化比较简单,整个过程就是创建对象,设置请求资源类型管理器以及优先级。ContentNegotiatingViewResolver 类实现了 ViewResolver 接口,但它并不直接解析视图,而是委托给其他解析器来完成。默认情况下,它是从 Spring 上下文查找视图解析器,并调用这些解析器。

BeanNameViewResolver 实例化源码如下:

  1. @Bean
  2. @ConditionalOnBean(View.class)
  3. @ConditionalOnMissingBean
  4. public BeanNameViewResolver beanNameViewResolver() {
  5. BeanNameViewResolver resolver = new BeanNameViewResolver();
  6. resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
  7. return resolver;
  8. }

BeanNameViewResolver 主要通过逻辑视图名称匹配定义好的视图 Bean 对象。一般情况下,对应的 Bean 对象需要注册到 Spring 的上下文中,BeanNameViewResolver 会返回名称匹配的视图对象。

2. 静态资源的支持

前端页面往往需要访问到静态资源,Spring Boot 对静态资源(比如图片、CSS、JS 等)的支持,也包括对 webjars 的支持,主要是通过实现接口 WebMvcConfigurer 的 addResourceHandlers 方法来完成的。

通过 WebMvcConfigurer 接口实现自定义配置是 Spring 内部的一种配置方式,它代替了传统的 XML 形式的配置。通过对该接口具体方法的实现,可以自定义一些 Handler、Interceptor、ViewResolver 等参数。

在 WebMvcAutoConfiguration 自动配置中,提供了一个 WebMvcAutoConfigurationAdapter 内部类,它实现了 WebMvcConfigurer 接口。而 Spring Boot 对静态资源的支持便是通过 WebMvcAutoConfigurationAdapter 实现了接口 WebMvcConfigurer 的 addResourceHandlers 方法来完成的。

  1. @Override
  2. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  3. // 如果默认资源处理器为不可用状态则返回
  4. if (!this.resourceProperties.isAddMappings()) {
  5. logger.debug("Default resource handling disabled");
  6. return;
  7. }
  8. Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
  9. CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
  10. // 针对webjars做了特殊的判断逻辑
  11. if (!registry.hasMappingForPattern("/webjars/**")) {
  12. // 如果不存在针对webjars的配置,则在此处添加,并设置默认路径等
  13. customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
  14. .addResourceLocations("classpath:/META-INF/resources/webjars/")
  15. .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
  16. }
  17. String staticPathPattern = this.mvcProperties.getStaticPathPattern();
  18. // 如果当前的ResourceHandlerRegistry里面资源映射没有"/**",则启用默认的静态资源处理
  19. if (!registry.hasMappingForPattern(staticPathPattern)) {
  20. customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
  21. .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
  22. .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
  23. }
  24. }

默认的静态资源映射路径在 ResourceProperties 类中定义,在上面代码中是通过 getStaticLocations 方法来获取的,具体代码如下:

  1. private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
  2. "classpath:/resources/", "classpath:/static/", "classpath:/public/" };
  3. public String[] getStaticLocations() {
  4. return this.staticLocations;
  5. }

至此我们可以看出,Spring Boot 默认会加载 classpath:/META-INF/resources/、classpath:/resources/、classpath:/static/、classpath:/public/ 路径下的静态资源。这是“约定”的一部分,也是为什么我们在实践中默认会将静态资源都放置在以上路径下。

Web 容器自动配置

我们知道,在使用 Spring Boot 时可以内嵌 Tomcat 等 Servlet 容器,通过直接执行 jar -jar 命令即可启动。那么 Spring Boot 是如何检测到对应的 Servlet 容器,又是如何进行自动配置的呢?下面就来分析 Spring Boot 集成 Servlet Web 容器的加载过程。

1. Servlet Web 服务器概述

我们先从整体上了解下 Spring Boot 对 Servlet Web 的支持,以及都包含哪些核心部分,如下图所示:
image.png
图中第一列为 Servlet 容器名称,表示 Spring Boot 内置支持的 Web 容器类型,目前包括 Tomcat、Jetty、Undertow。第二列为针对不同的 Web 容器的 WebServer 实现类,用于控制 Web 容器的启动和停止等操作。第三列为创建第二列中具体 WebServer 的工厂类。

以 Tomcat 为例,通过自动配置先初始化 TomcatServletWebServerFactory 工厂类,在 Spring Boot 启动过程中,该工厂类会通过其 getWebServer 方法创建 TomcatWebServer 实例,启动 Tomcat 等一系列操作。

2. ServletWebServerFactoryAutoConfiguration

在 Spring Boot 中,Servlet Web 容器的核心配置就是上面提到的三个工厂方法的实例化和 BeanPostProcessor的注册。之前在讲 DispatcherServletAutoConfiguration 自动配置时,我们并没有详细讲解其中的 @AutoConfigureAfter 注解,该注解内指定的类为 ServletWebServerFactoryAutoConfiguration,即在完成了 Web Server 容器的自动配置之后,才会进行 DispatcherServlet 的自动配置。

ServletWebServerFactoryAutoConfiguration 是用来自动配置 Servlet 的 Web 服务的,下面先看其注册部分的源代码。具体如下:

  1. @Configuration(proxyBeanMethods = false)
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. // 需要存在ServletRequest类
  4. @ConditionalOnClass(ServletRequest.class)
  5. // 需要Web类型为Servlet类型
  6. @ConditionalOnWebApplication(type = Type.SERVLET)
  7. // 加载ServerProperties中的配置
  8. @EnableConfigurationProperties(ServerProperties.class)
  9. // 导入内部类BeanPostProcessorsRegistrar用来注册BeanPostProcessors
  10. // 导入ServletWebServerFactoryConfiguration的三个内部类,用来判断应用服务器类型
  11. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  12. ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  13. ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  14. ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  15. public class ServletWebServerFactoryAutoConfiguration {}

我们重点看下 @lmport 注解中引入的内容,该注解引入了当前类的内部类 BeanPostProcessorsRegistrar 和 ServletWebServerFactoryConfiguration 的三个内部类:EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow。

2.1 ServletWebServerFactoryConfiguration

先来看 ServletWebServerFactoryConfiguration 类,它是 Servlet Web 服务器的配置类,目前该类中包含了内置 Tomcat、Jetty 和 Undertow 的配置,重点作用就是实例化上图中的那些工厂类。具体就是在方法内通过 new 创建对应的工厂类,并设置其初始化参数,然后注入 Spring 容器中。下面以 Tomcat 容器为例来进行源码层面的讲解。EmbeddedTomcat 内部类的代码如下:

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
  3. @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
  4. public static class EmbeddedTomcat {
  5. @Bean
  6. public TomcatServletWebServerFactory tomcatServletWebServerFactory(
  7. ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
  8. ObjectProvider<TomcatContextCustomizer> contextCustomizers,
  9. ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
  10. TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
  11. factory.getTomcatConnectorCustomizers()
  12. .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
  13. factory.getTomcatContextCustomizers()
  14. .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
  15. factory.getTomcatProtocolHandlerCustomizers()
  16. .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
  17. return factory;
  18. }
  19. }

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

TomcatServletWebServerFactory 的实例化方法由三个 ObjectProvider 参数构成。ObjectProvider 参数中的泛型依次包括 TomcatConnectorCustomizer、TomcatContextCustomizer 和 TomcatProtocolHandlerCustomizer<?>,它们均为回调接口,用于针对 Tomcat 执行一些定制化的逻辑。

而 ObjectProvider 接口是 ObjectFactory 的一种变体,专门为注入设计的。在正常情况下,如果构造方法依赖某个 Bean,则需通过 @Autowired 进行注入,并且在单构造函数时可以默认省略掉 @Autowired 隐式注入。但如果待注入的参数的 Bean 为空或有多个时,便是 ObjectProvider 发挥作用的时候了。如果注入实例为空,使用 ObjectProvider 则避免了强依赖导致的依赖对象不存在;如果有多个实例,ObjectProvider 会根据 Bean 实现的 Ordered 接口或 @Order 注解指定的先后顺序获取一个 Bean,从而提供一个更加宽松的依赖注入方式。

TomcatServletWebServerFactory 的实例化代码非常简单,只是调用了无参的构造方法,在构造过程中根据默认值进行属性填充,最终生成一个 WebServerFactory 接口的实现类。

2.2 BeanPostProcessorsRegistrar

下面,我们继续看 ServletWebServerFactoryAutoConfiguration 引入的另外一个类:BeanPostProcessorsRegistrar,注意它是当前自动配置类的一个内部类,源码如下:

  1. // 通过实现ImportBeanDefinitionRegistrar接口来注册一个WebServerFactoryCustomizerBeanPostProcessor处理器
  2. public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
  3. private ConfigurableListableBeanFactory beanFactory;
  4. // 实现BeanFactoryAware接口,设置beanFactory属性
  5. @Override
  6. public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  7. if (beanFactory instanceof ConfigurableListableBeanFactory) {
  8. this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
  9. }
  10. }
  11. // 注册一个WebServerFactoryCustomizerBeanPostProcessor
  12. @Override
  13. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  14. registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
  15. registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
  16. }
  17. ......
  18. }

我们知道 Spring 在注册 Bean 时,大多都使用 ImportBeanDefinitionRegistrar 接口来实现。这里的 Bean 注册逻辑可以说是按照 Spring 官方模式来进行的。一般情况下,我们先定义一个 ImportBeanDefinitionRegistrar 接口的实现类,然后在有 @Configuration 注解的配置类上使用 @lmport 导入该实现类。其中,在实现类的 registerBeanDefinitions 方法中实现具体 Bean 的注册功能。在实现 ImportBeanDefinitionRegistrar 接口的同时,还可以实现 BeanFactoryAware 接口,用来设置用于检查 Bean 是否存在的 BeanFactory。具体完成 Bean 的实例化,并向容器中注册 Bean 则是由 RootBeanDefinition 来完成的。

WebServerFactoryCustomizerBeanPostProcessor 的作用主要是在 WebServerFactory 初始化时获取自动配置类注入的 WebServerFactoryCustomizer,然后分别调用其 customize 方法来进行 WebServerFactory 的定制处理。ErrorPageRegistrarBeanPostProcessor 的作用是搜集容器中的 ErrorPageRegistrar,添加到当前应用所采用的 ErrorPageRegistry 中。至此,ServletWebServerFactoryAutoConfiguration 注解部分讲解完毕。

WebServer 初始化

上面讲到 Spring Boot 通过自动配置初始化了 WebServer 对应的工厂类,下面重点讲解 WebServer 是如何被初始化,又是如何启动的。WebServer 接口的源码如下:

  1. public interface WebServer {
  2. // 启动Web容器
  3. void start() throws WebServerException;
  4. // 停止Web容器
  5. void stop() throws WebServerException;
  6. // 获取容器端口
  7. int getPort();
  8. }

下面以 Tomcat 的启动为例来说明整个内置容器的加载与启动。这要回到最初讲到的 Spring Boot 的启动过程,在 SpringApplication 的 run 方法中有一个调用容器初始化的 refreshContext 方法,而 refreshContext 中又调用了 refresh 方法:

  1. protected void refresh(ApplicationContext applicationContext) {
  2. Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
  3. ((AbstractApplicationContext) applicationContext).refresh();
  4. }

这里的 applicationContext 是在之前的 createApplicationContext 方法中创建的,根据当前的 Web 类型默认创建的容器为 AnnotationConfigServletWebServerApplicationContext 对象。

  1. protected ConfigurableApplicationContext createApplicationContext() {
  2. Class<?> contextClass = this.applicationContextClass;
  3. if (contextClass == null) {
  4. try {
  5. switch (this.webApplicationType) {
  6. case SERVLET:
  7. contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
  8. break;
  9. case REACTIVE:
  10. contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
  11. break;
  12. default:
  13. contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
  14. }
  15. }
  16. }
  17. return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  18. }

因此,在上面的 refresh 方法中实际调用的就是这个容器的 refresh 方法,但该类并没有重写 refresh 方法,因此具体实现在其父类 ServletWebServerApplicationContext 类中,ServletWebServerApplicationContext 中的 refresh 方法仅调用了其父类 AbstractApplicationContext 的 refresh 方法,具体如下:

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. // Initialize other special beans in specific context subclasses.
  4. onRefresh();
  5. ......
  6. }

这里只关注 onRefresh 方法,onRefresh 方法是留给子类实现的,默认不做任何操作,因此该方法真正的实现逻辑又回到了其子类 ServletWebServerApplicationContext 中,具体如下:

  1. @Override
  2. protected void onRefresh() {
  3. ......
  4. createWebServer();
  5. ......
  6. }

至此,我们发现了真正执行创建 Web 容器的地方。在 createWebServer 方法中,初始化时默认 webServer 和 servletContext 都是 null,因此直接通过 ServletWebServerFactory 调用其 getWebServer 方法来创建容器:

  1. private void createWebServer() {
  2. WebServer webServer = this.webServer;
  3. ServletContext servletContext = getServletContext();
  4. if (webServer == null && servletContext == null) {
  5. ServletWebServerFactory factory = getWebServerFactory();
  6. this.webServer = factory.getWebServer(getSelfInitializer());
  7. }
  8. ......
  9. }

这里获得的 ServletWebServerFactory 的具体实现类,正是我们通过自动配置机制实例化的 TomcatServletWebServerFactory,它对应的 getWebServer 的具体实现如下:

  1. @Override
  2. public WebServer getWebServer(ServletContextInitializer... initializers) {
  3. // 创建Tomcat容器
  4. Tomcat tomcat = new Tomcat();
  5. // 获取并设置baseDir路径,不存在则创建一个以tomcat为前缀的临时文件
  6. File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
  7. tomcat.setBaseDir(baseDir.getAbsolutePath());
  8. // 创建Connector组件
  9. Connector connector = new Connector(this.protocol);
  10. connector.setThrowOnFailure(true);
  11. tomcat.getService().addConnector(connector);
  12. // Connector定制化
  13. customizeConnector(connector);
  14. tomcat.setConnector(connector);
  15. tomcat.getHost().setAutoDeploy(false);
  16. configureEngine(tomcat.getEngine());
  17. for (Connector additionalConnector : this.additionalTomcatConnectors) {
  18. tomcat.getService().addConnector(additionalConnector);
  19. }
  20. prepareContext(tomcat.getHost(), initializers);
  21. // 创建TomcatWebServer
  22. return getTomcatWebServer(tomcat);
  23. }

在该方法中实现了 Tomcat 的创建、BaseDir 的设置、Connector 的初始化和定制化等一系列初始化操作以及 TomcatWebServer 的创建和初始化。下面分析下 TomcatWebServer 的初始化逻辑:

  1. private void initialize() throws WebServerException {
  2. synchronized (this.monitor) {
  3. try {
  4. // 将实例id添加到tomcat引擎名字中,格式为:原引擎名字-实例id
  5. addInstanceIdToEngineName();
  6. // 从Tomcat的host中获取子Context
  7. Context context = findContext();
  8. // 添加生命周期的监听事件
  9. context.addLifecycleListener((event) -> {
  10. if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
  11. // 移除connector,确保当服务器启动时不会进行协议绑定
  12. removeServiceConnectors();
  13. }
  14. });
  15. // 启动Tomcat服务,触发初始化监听
  16. this.tomcat.start();
  17. // 可以直接在主线程中重新抛出失败异常,TomcatStarter不存在或状态错误均会抛出异常
  18. rethrowDeferredStartupExceptions();
  19. try {
  20. // 绑定一个命名的context到类加载器
  21. ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
  22. } catch (NamingException ex) {}
  23. // 和Jetty不同, 所有Tomcat线程都是守护线程,这里创建一个非守护线程用于等待关闭容器(阻塞等待,直到接收到关闭容器的命令)
  24. startDaemonAwaitThread();
  25. }
  26. ......
  27. }
  28. }

至此,针对 Tomcat 的 TomcatWebServer 的初始化已经完成,其他 Web 容器的思路也是类似的。