Spring Boot 内嵌了 Web 容器,默认为 tomcat,我们可以通过修改配置,来实现自定义 Web 容器,目前 Spring Boot 默认支持,tomcat,jetty,netty,undertow 四种 Web 容器
自动配置 Web 容器
默认情况下,我们可以通过 application.yml 文件来修改 web 容器的相关参数,如:
server:port: 80tomcat:uri-encoding: utf-8connection-timeout: 10000
同样我们也可以使用 @Bean 的方式来进行配置
// 方式一:自定义 web 容器@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory() {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();// 这一步其实就是实现了 server.port = 80factory.setPort(80);return factory;}// 方式二:自定义 web 容器@Beanpublic WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer(){WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer = new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {// factory: 当前正在使用的 web 容器@Overridepublic void customize(TomcatServletWebServerFactory factory) {factory.setPort(80);factory.setUriEncoding(StandardCharsets.UTF_8);}};return webServerFactoryCustomizer;}
Spring Boot 如何实现自动配置
那么 Spring Boot 是如何实现自动配置 Web 容器的呢?我们还是找到了 Spring Boot 的 autoconfigure 包,在 spring.factories 文件中,我们找了了 ServletWebServerFactoryAutoConfiguration 类,就是这个类完成了 Web 容器的自动配置
@Configuration(proxyBeanMethods = false)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@ConditionalOnClass(ServletRequest.class)@ConditionalOnWebApplication(type = Type.SERVLET)@EnableConfigurationProperties(ServerProperties.class)@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })public class ServletWebServerFactoryAutoConfiguration {// a lot of bean}
我们可以看到 @Import 注解默认加入了 **EmbeddedTomcat.class、EmbeddedJetty.class 和 EmbeddedUndertow.class**,三个 Web 容器,我们在看 EmbeddedTomcat.class 做了那些事
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> 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;}}
我们可以看到,@Bean 中,其实就是 new 了一个 TomcatServletWebServerFactory 对象,返回给容器,同时它有一个非常重要的注解,@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT),这个注解的意思是:如果容器中没有 ServletWebServerFactory.class 这个类,那么就会加载这个 Bean,反之,如果有了,就不会加载
同理下面的 EmbeddedJetty.class 和 EmbeddedUndertow.class 也遵守这个规则,所以 Spring Boot 只会加载一个 Web 容器
那么问题来了,为什么 tomcat 是默认的容器呢?
为什么 Tomcat 是默认 Web 容器
其实这个很简单,我们再看一下 ServletWebServerFactoryAutoConfiguration 这个类上的注解
@Configuration(proxyBeanMethods = false)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@ConditionalOnClass(ServletRequest.class)@ConditionalOnWebApplication(type = Type.SERVLET)@EnableConfigurationProperties(ServerProperties.class)@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })public class ServletWebServerFactoryAutoConfiguration {// a lot of bean}
@Import 导入的是一个数组,先导入 EmbeddedTomcat.class,再导入 EmbeddedJetty.class,最后导入 EmbeddedUndertow.class,既然是数组,就有先后顺序,所以 Tomcat 是 Spring Boot 的默认 Web 容器
**
BeanPostProcessorsRegistrar.class 导入的这个类,主要是处理一些我们利用 WebServerFactoryCustomizer 来自定义配置的相关的参数
如何使用其他 Web 容器
我们现在知道 Spring Boot 默认使用 tomcat 作为 Web 容器,所以,我们再加入 Jetty 依赖的时候,还需要剔除 Tomcat 的依赖,否则由于上述的顺序加载的问题,还会使用 Tomcat 作为默认容器
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jetty --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency>
已经使用 jetty 作为 Web 容器了
