springboot自动配置类ServletWebServerFactoryAutoConfiguration

164 阅读5分钟

spring-boot-autoconfigure包下META-INF文件下的spring-autoconfigure-metadata.properties文件指出自动配置类的加载前后顺序以及条件.

META-INF文件下还有一个spring.factories,里面将所有springboot的自动配置类涵盖在里面了.springboot启动的时候会去加载该文件,然后根据自动配置类的条件注解是否满足选择将相应的Bean注入到Spring容器中. 以上并非本文的重点,上面的内容后续可以整理成一篇单独的博客.

让我们回到本文的主角ServletWebServerFactoryAutoConfiguration.

ServletWebServerFactoryAutoConfiguration在spring.factories中能被找到.

package org.springframework.boot.autoconfigure.web.servlet;

// 省略 import 导入依赖行

/**
 * EnableAutoConfiguration Auto-configuration for servlet web servers.
 *
 */
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 仅在类 ServletRequest 存在于 classpath 上时才生效(javax.servlet.ServletRequest,需要导入tomcat相关包org.apache.tomcat.embed:tomcat-embed-core)
@ConditionalOnClass(ServletRequest.class)
// 仅在当前应用是 Servlet Web 应用时才生效(关于ConditionalOnWebApplication注解的源码后续单独开博文描述)
@ConditionalOnWebApplication(type = Type.SERVLET)
// 确保yml配置文件中前缀为 server 的配置参数加载到 bean ServerProperties
@EnableConfigurationProperties(ServerProperties.class)
// 1. 通过ImportBeanDefinitionRegistrar的实现类方式导入 ServletWebServerFactoryAutoConfiguration.
// BeanPostProcessorsRegistrar 以注册
// BeanPostProcessor : WebServerFactoryCustomizerBeanPostProcessor 和 
// ErrorPageRegistrarBeanPostProcessor
// 2. 直接导入 EmbeddedTomcat/EmbeddedJetty/EmbeddedUndertow 这三个属于
// ServletWebServerFactoryConfiguration 的嵌套配置类,这三个配置类会分别检测
// classpath上存在的类,从而判断当前应用使用的是 Tomcat/Jetty/Undertow,
// 从而决定定义哪一个 Servlet Web服务器的工厂 bean :
// TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    // 定义 bean ServletWebServerFactoryCustomizer
    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        //根据serverProperties的自定义配置创建一个ServletWebServerFactoryCustomizer
        //主要设置Port,Address,,Compression,ServerHeader,ContextParameters等参数
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }

    // 针对当前Servlet容器是Tomcat时定义该 bean,用于定制化 TomcatServletWebServerFactory 
    @Bean
    // 仅在类 org.apache.catalina.startup.Tomcat 存在于 classpath 上时才生效,
    //需要导入tomcat相关包org.apache.tomcat.embed:tomcat-embed-core
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        //根据serverProperties的自定义配置创建一个TomcatServletWebServerFactoryCustomizer
        //主要用于设置Tomcat相关的参数比如additionalTldSkipPatterns,redirectContextRoot,
        //useRelativeRedirects
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    /**
     * Registers a WebServerFactoryCustomizerBeanPostProcessor. Registered via
     * ImportBeanDefinitionRegistrar for early registration.
     * 这是一个 ImportBeanDefinitionRegistrar(参见@Import注解的三种用法), 
     * 它会向容器注入两个 BeanPostProcessor :
     * 1. WebServerFactoryCustomizerBeanPostProcessor
     * 该 BeanPostProcessor 会搜集容器中所有的 WebServerFactoryCustomizer,对当前应用所采用的 
     * WebServerFactory 被初始化前进行定制
     * 2. ErrorPageRegistrarBeanPostProcessor
     * 该 BeanPostProcessor 会搜集容器中所有的 ErrorPageRegistrar,添加到当前应用所采用的 
     * ErrorPageRegistry 中,实际上,这里的 ErrorPageRegistry 会是 ConfigurableWebServerFactory,
     * 具体实现上来讲,是一个 ConfigurableTomcatWebServerFactory,ConfigurableJettyWebServerFactory
     * 或者 ConfigurableUndertowWebServerFactory,分别对应 Tomcat, Jetty, Undertow 这三种
     * Servlet Web 容器的工厂类
     */
    public static class BeanPostProcessorsRegistrar
            implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

        private ConfigurableListableBeanFactory beanFactory;

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
            }
        }

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                BeanDefinitionRegistry registry) {
            if (this.beanFactory == null) {
                return;
            }
            registerSyntheticBeanIfMissing(registry,
                    "webServerFactoryCustomizerBeanPostProcessor",
                    WebServerFactoryCustomizerBeanPostProcessor.class);
            registerSyntheticBeanIfMissing(registry,
                    "errorPageRegistrarBeanPostProcessor",
                    ErrorPageRegistrarBeanPostProcessor.class);
        }

        private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
                String name, Class<?> beanClass) {
            //spring容器中不存在WebServerFactoryCustomizerBeanPostProcessor实例情况下注入
            if (ObjectUtils.isEmpty(
                    this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(name, beanDefinition);
            }
        }

    }

}

ServletWebServerFactoryConfiguration



package org.springframework.boot.autoconfigure.web.servlet;
// 省略 import 导入依赖行

class ServletWebServerFactoryConfiguration {

   @Configuration
   //pom.xml中引入了spring-boot-starter-web启动器会导入tomcat包,以下条件得到满足
   @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   public static class EmbeddedTomcat {
        
      //创建了一个TomcatServletWebServerFactory注入到spring容器中,
      //后面讲述springboot如何内嵌一个Tomcat服务时会讲到这个类
      @Bean
      public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
         return new TomcatServletWebServerFactory();
      }

   }

   /**
    * Nested configuration if Jetty is being used.
    */
   @Configuration
   //pom.xml中引入了spring-boot-starter-web启动器会导入tomcat包,以下条件不满足
   //缺少Server,Loader,WebAppContext
   @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
         WebAppContext.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   public static class EmbeddedJetty {

      @Bean
      public JettyServletWebServerFactory JettyServletWebServerFactory() {
         return new JettyServletWebServerFactory();
      }

   }

   /**
    * Nested configuration if Undertow is being used.
    */
   @Configuration
   //pom.xml中引入了spring-boot-starter-web启动器会导入tomcat包,以下条件不满足
   //缺少Undertow,SslClientAuthMode
   @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   public static class EmbeddedUndertow {

      @Bean
      public UndertowServletWebServerFactory undertowServletWebServerFactory() {
         return new UndertowServletWebServerFactory();
      }

   }

}

ServletWebServerFactoryCustomizer

package org.springframework.boot.autoconfigure.web.servlet;

// 省略 import 导入依赖行

public class ServletWebServerFactoryCustomizer implements
      WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

   private final ServerProperties serverProperties;

   public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
      //yml文件中自定义配置
      this.serverProperties = serverProperties;
   }

   @Override
   public int getOrder() {
      return 0;
   }

   //配置相关属性,该方法的调用在WebServerFactoryCustomizerBeanPostProcessor中的生命周期钩子时被触发
   @Override
   public void customize(ConfigurableServletWebServerFactory factory) {
      PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
      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()::getApplicationDisplayName)
            .to(factory::setDisplayName);
      map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
      map.from(this.serverProperties::getSsl).to(factory::setSsl);
      map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
      map.from(this.serverProperties::getCompression).to(factory::setCompression);
      map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
      map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
      map.from(this.serverProperties.getServlet()::getContextParameters)
            .to(factory::setInitParameters);
   }

}

TomcatServletWebServerFactoryCustomizer

package org.springframework.boot.autoconfigure.web.servlet;

// 省略 import 导入依赖行
public class TomcatServletWebServerFactoryCustomizer
      implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {

   private final ServerProperties serverProperties;

   public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
      this.serverProperties = serverProperties;
   }

   @Override
   public int getOrder() {
      return 0;
   }

   @Override
   public void customize(TomcatServletWebServerFactory factory) {
      ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
      if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
         factory.getTldSkipPatterns()
               .addAll(tomcatProperties.getAdditionalTldSkipPatterns());
      }
      if (tomcatProperties.getRedirectContextRoot() != null) {
         customizeRedirectContextRoot(factory,
               tomcatProperties.getRedirectContextRoot());
      }
      if (tomcatProperties.getUseRelativeRedirects() != null) {
         customizeUseRelativeRedirects(factory,
               tomcatProperties.getUseRelativeRedirects());
      }
   }
   
   //addContextCustomizers(TomcatContextCustomizer tomcatContextCustomizer)
   //涉及到tomcat的知识,暂未了解
   private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory,
         boolean redirectContextRoot) {
      factory.addContextCustomizers((context) -> context
            .setMapperContextRootRedirectEnabled(redirectContextRoot));
   }
   //addContextCustomizers(TomcatContextCustomizer tomcatContextCustomizer)
   //涉及到tomcat的知识,暂未了解
   private void customizeUseRelativeRedirects(ConfigurableTomcatWebServerFactory factory,
         boolean useRelativeRedirects) {
      factory.addContextCustomizers(
            (context) -> context.setUseRelativeRedirects(useRelativeRedirects));
   }

}

WebServerFactoryCustomizerBeanPostProcessor

// 省略 import 导入依赖行

package org.springframework.boot.web.server;

public class WebServerFactoryCustomizerBeanPostProcessor
      implements BeanPostProcessor, BeanFactoryAware {

   private ListableBeanFactory beanFactory;

   private List<WebServerFactoryCustomizer<?>> customizers;

   @Override
   public void setBeanFactory(BeanFactory beanFactory) {
      Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
            "WebServerCustomizerBeanPostProcessor can only be used "
                  + "with a ListableBeanFactory");
      this.beanFactory = (ListableBeanFactory) beanFactory;
   }

   //生命周期钩子,在spring容器启动后会被调用
   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName)
         throws BeansException {
      if (bean instanceof WebServerFactory) {
         //执行下面的postProcessBeforeInitialization方法
         postProcessBeforeInitialization((WebServerFactory) bean);
      }
      return bean;
   }

   @Override
   public Object postProcessAfterInitialization(Object bean, String beanName)
         throws BeansException {
      return bean;
   }

   @SuppressWarnings("unchecked")
   private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
      LambdaSafe
            //执行下面的getCustomizers方法
            .callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
                  webServerFactory)
            .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
            .invoke((customizer) -> customizer.customize(webServerFactory));
   }

   private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
      if (this.customizers == null) {
         //执行下面的getWebServerFactoryCustomizerBeans方法
         // Look up does not include the parent context
         this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
         this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
         this.customizers = Collections.unmodifiableList(this.customizers);
      }
      return this.customizers;
   }

   @SuppressWarnings({ "unchecked", "rawtypes" })
   private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
      //获取到我们在上面注入的WebServerFactoryCustomizer的两个子类ServletWebServerFactoryCustomizer,
      //TomcatServletWebServerFactoryCustomizer
      return (Collection) this.beanFactory
            .getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
   }

}

总结:如果我们在开发中是servlet环境,且引入了tomcat相关包org.apache.tomcat.embed:tomcat-embed-core(spring-boot-starter-web->spring-boot-starter-tomcat)

ServletWebServerFactoryAutoConfiguration自动配置类会注入WebServerFactoryCustomizer的两个子类ServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizer,同时会经由@Import注入ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class的方式将WebServerFactoryCustomizerBeanPostProcessor注入到容器中,正是WebServerFactoryCustomizerBeanPostProcessor的生命周期钩子中调用了WebServerFactoryCustomizer的自定义函数,实现对servlet,tomcat容器的自定义化.