Spring Boot 内嵌了 Web 容器,默认为 tomcat,我们可以通过修改配置,来实现自定义 Web 容器,目前 Spring Boot 默认支持,tomcat,jetty,netty,undertow 四种 Web 容器

自动配置 Web 容器

默认情况下,我们可以通过 application.yml 文件来修改 web 容器的相关参数,如:

  1. server:
  2. port: 80
  3. tomcat:
  4. uri-encoding: utf-8
  5. connection-timeout: 10000

同样我们也可以使用 @Bean 的方式来进行配置

  1. // 方式一:自定义 web 容器
  2. @Bean
  3. public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
  4. TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
  5. // 这一步其实就是实现了 server.port = 80
  6. factory.setPort(80);
  7. return factory;
  8. }
  9. // 方式二:自定义 web 容器
  10. @Bean
  11. public WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer(){
  12. WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer = new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
  13. // factory: 当前正在使用的 web 容器
  14. @Override
  15. public void customize(TomcatServletWebServerFactory factory) {
  16. factory.setPort(80);
  17. factory.setUriEncoding(StandardCharsets.UTF_8);
  18. }
  19. };
  20. return webServerFactoryCustomizer;
  21. }

Spring Boot 如何实现自动配置

那么 Spring Boot 是如何实现自动配置 Web 容器的呢?我们还是找到了 Spring Boot 的 autoconfigure 包,在 spring.factories 文件中,我们找了了 ServletWebServerFactoryAutoConfiguration 类,就是这个类完成了 Web 容器的自动配置

  1. @Configuration(proxyBeanMethods = false)
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. @ConditionalOnClass(ServletRequest.class)
  4. @ConditionalOnWebApplication(type = Type.SERVLET)
  5. @EnableConfigurationProperties(ServerProperties.class)
  6. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  7. ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  8. ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  9. ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  10. public class ServletWebServerFactoryAutoConfiguration {
  11. // a lot of bean
  12. }

我们可以看到 @Import 注解默认加入了 **EmbeddedTomcat.class、EmbeddedJetty.class 和 EmbeddedUndertow.class**,三个 Web 容器,我们在看 EmbeddedTomcat.class 做了那些事

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
  3. @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
  4. static class EmbeddedTomcat {
  5. @Bean
  6. 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. }

我们可以看到,@Bean 中,其实就是 new 了一个 TomcatServletWebServerFactory 对象,返回给容器,同时它有一个非常重要的注解,@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT),这个注解的意思是:如果容器中没有 ServletWebServerFactory.class 这个类,那么就会加载这个 Bean,反之,如果有了,就不会加载

同理下面的 EmbeddedJetty.class 和 EmbeddedUndertow.class 也遵守这个规则,所以 Spring Boot 只会加载一个 Web 容器

那么问题来了,为什么 tomcat 是默认的容器呢?

为什么 Tomcat 是默认 Web 容器

其实这个很简单,我们再看一下 ServletWebServerFactoryAutoConfiguration 这个类上的注解

  1. @Configuration(proxyBeanMethods = false)
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. @ConditionalOnClass(ServletRequest.class)
  4. @ConditionalOnWebApplication(type = Type.SERVLET)
  5. @EnableConfigurationProperties(ServerProperties.class)
  6. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  7. ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  8. ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  9. ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  10. public class ServletWebServerFactoryAutoConfiguration {
  11. // a lot of bean
  12. }

@Import 导入的是一个数组,先导入 EmbeddedTomcat.class,再导入 EmbeddedJetty.class,最后导入 EmbeddedUndertow.class,既然是数组,就有先后顺序,所以 Tomcat 是 Spring Boot 的默认 Web 容器
**

BeanPostProcessorsRegistrar.class 导入的这个类,主要是处理一些我们利用 WebServerFactoryCustomizer 来自定义配置的相关的参数

**

如何使用其他 Web 容器

我们现在知道 Spring Boot 默认使用 tomcat 作为 Web 容器,所以,我们再加入 Jetty 依赖的时候,还需要剔除 Tomcat 的依赖,否则由于上述的顺序加载的问题,还会使用 Tomcat 作为默认容器

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. <exclusions>
  5. <exclusion>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-tomcat</artifactId>
  8. </exclusion>
  9. </exclusions>
  10. </dependency>
  11. <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jetty -->
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-jetty</artifactId>
  15. </dependency>

已经使用 jetty 作为 Web 容器了
image.png