Spring Boot 内嵌了 Web 容器,默认为 tomcat,我们可以通过修改配置,来实现自定义 Web 容器,目前 Spring Boot 默认支持,tomcat,jetty,netty,undertow 四种 Web 容器
自动配置 Web 容器
默认情况下,我们可以通过 application.yml 文件来修改 web 容器的相关参数,如:
server:
port: 80
tomcat:
uri-encoding: utf-8
connection-timeout: 10000
同样我们也可以使用 @Bean 的方式来进行配置
// 方式一:自定义 web 容器
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// 这一步其实就是实现了 server.port = 80
factory.setPort(80);
return factory;
}
// 方式二:自定义 web 容器
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer(){
WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer = new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
// factory: 当前正在使用的 web 容器
@Override
public 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 {
@Bean
TomcatServletWebServerFactory 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 容器了