每个Spring Boot Web应用程序都包含一个嵌入式Web服务器。此功能导致许多方法问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。本节回答这些问题。

3.1. 使用其他Web服务器

许多Spring Boot启动器都包含默认的嵌入式容器。

  • 对于servlet堆栈应用程序,通过spring-boot-starter-web包括来包括Tomcat spring-boot-starter-tomcat,但是您可以使用spring-boot-starter-jettyspring-boot-starter-undertow代替。
  • 对于反应栈的应用,spring-boot-starter-webflux包括反应堆的Netty通过包括spring-boot-starter-reactor-netty,但你可以使用spring-boot-starter-tomcatspring-boot-starter-jettyspring-boot-starter-undertow代替。

切换到其他HTTP服务器时,您需要将默认依赖项交换为所需的依赖项。为了帮助完成此过程,Spring Boot为每个受支持的HTTP服务器提供了一个单独的启动器。
以下Maven示例显示了如何排除Tomcat并包括Jetty for Spring MVC:

  1. <properties>
  2. <servlet-api.version>3.1.0</servlet-api.version>
  3. </properties>
  4. <dependency>
  5. <groupId>org.springframework.boot</groupId>
  6. <artifactId>spring-boot-starter-web</artifactId>
  7. <exclusions>
  8. <!-- Exclude the Tomcat dependency -->
  9. <exclusion>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-tomcat</artifactId>
  12. </exclusion>
  13. </exclusions>
  14. </dependency>
  15. <!-- Use Jetty instead -->
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-jetty</artifactId>
  19. </dependency>
Servlet API的版本已被覆盖,因为与Tomcat 9和Undertow 2.0不同,Jetty 9.4不支持Servlet 4.0。

以下Gradle示例显示了如何使用Undertow代替Spring WebFlux的Reactor Netty:

  1. configurations.all {
  2. resolutionStrategy.dependencySubstitution.all { dependency ->
  3. if (dependency.requested instanceof ModuleComponentSelector && dependency.requested.module == 'spring-boot-starter-reactor-netty') {
  4. dependency.useTarget("org.springframework.boot:spring-boot-starter-undertow:$dependency.requested.version", 'Use Undertow instead of Reactor Netty')
  5. }
  6. }
  7. }
  8. dependencies {
  9. compile 'org.springframework.boot:spring-boot-starter-webflux'
  10. // ...
  11. }
spring-boot-starter-reactor-netty使用WebClient该类是必需的,因此即使您需要包括其他HTTP服务器,也可能需要保持对Netty的依赖。

3.2. 禁用Web服务器

如果您的类路径包含启动Web服务器所需的位,则Spring Boot将自动启动它。要禁用此行为,请WebApplicationType在中配置application.properties,如以下示例所示:
物产
Yaml

  1. spring.main.web-application-type=none

3.3. 更改HTTP端口

在独立应用程序中,主HTTP端口默认为,8080但可以使用server.port(例如,在application.propertiesSystem属性中或作为System属性)进行设置。由于轻松地绑定了Environment值,因此还可以使用SERVER_PORT(例如,作为OS环境变量)。
要完全关闭HTTP端点但仍创建一个WebApplicationContext,请使用server.port=-1(这样做有时对测试很有用)。
有关更多详细信息,请参阅“ Spring Boot功能”部分中的“ spring-boot-features.html ”或ServerProperties源代码。

3.4. 使用随机未分配的HTTP端口

要扫描可用端口(使用OS本机来防止冲突),请使用server.port=0

3.5. 在运行时发现HTTP端口

您可以从日志输出或WebServerApplicationContext通过其端口访问服务器运行的端口WebServer。最好的方法是确保它已被初始化,是添加一个@BeantypeApplicationListener<WebServerInitializedEvent>并在事件发布时将其从事件中拉出。
使用的测试@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)还可以通过使用@LocalServerPort批注将实际端口注入字段中,如以下示例所示:

  1. @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
  2. public class MyWebIntegrationTests {
  3. @LocalServerPort
  4. int port;
  5. // ...
  6. }
@LocalServerPort是的元注释@Value("${local.server.port}")。不要尝试在常规应用程序中注入端口。如我们所见,仅在初始化容器之后才设置该值。与测试相反,应早处理应用程序代码回调(在值实际可用之前)。

3.6. 启用HTTP响应压缩

Jetty,Tomcat和Undertow支持HTTP响应压缩。可以在中启用它application.properties,如下所示:
物产
Yaml

  1. server.compression.enabled=true

默认情况下,响应的长度必须至少为2048个字节才能执行压缩。您可以通过设置server.compression.min-response-size属性来配置此行为。
默认情况下,仅当响应的内容类型为以下之一时,它们才被压缩:

  • text/html
  • text/xml
  • text/plain
  • text/css
  • text/javascript
  • application/javascript
  • application/json
  • application/xml

您可以通过设置server.compression.mime-types属性来配置此行为。

3.7. 配置SSL

可以通过设置各种server.ssl.*属性来声明性地配置SSL ,通常在application.properties或中application.yml。以下示例显示了在中设置SSL属性application.properties
物产
Yaml

  1. server.port=8443
  2. server.ssl.key-store=classpath:keystore.jks
  3. server.ssl.key-store-password=secret
  4. server.ssl.key-password=another-secret

有关Ssl所有受支持属性的详细信息,请参见。
使用上述示例的配置意味着应用程序不再在端口8080上支持纯HTTP连接器。SpringBoot不支持通过进行HTTP连接器和HTTPS连接器的配置application.properties。如果要同时拥有两者,则需要以编程方式配置它们之一。我们建议您使用application.propertiesHTTPS进行配置,因为HTTP连接器是两者中以编程方式配置的比较容易的方法。

3.8. 配置HTTP / 2

您可以使用server.http2.enabled配置属性在Spring Boot应用程序中启用HTTP / 2支持。该支持取决于所选的Web服务器和应用程序环境,因为并非所有JDK8版本都立即支持该协议。

Spring Boot不建议使用h2cHTTP / 2协议的明文版本。因此,以下各节要求您首先配置SSL。如果仍然选择使用h2c,则可以查看专用部分

3.8.1. Tomcat的HTTP / 2

默认情况下,Spring Boot随Tomcat 9.0.x一起提供,当使用JDK 9或更高版本时,Tomcat 9.0.x支持HTTP / 2。另外,如果libtcnative库及其依赖项已安装在主机操作系统上,则可以在JDK 8上使用HTTP / 2 。
如果没有,则必须使库目录可用于JVM库路径。您可以使用JVM参数(例如)来执行此操作-Djava.library.path=/usr/local/opt/tomcat-native/lib。有关更多信息,请参见Tomcat官方文档
在没有该本机支持的情况下,在JDK 8上启动Tomcat 9.0.x会记录以下错误:
错误8787-[[main] oacoyote.http11.Http11NioProtocol:[h2]的升级处理程序[org.apache.coyote.http2.Http2Protocol]仅支持通过ALPN进行升级,但已为[“ https-jsse-nio”配置-8443“]连接器不支持ALPN。
此错误不是致命错误,并且该应用程序仍以HTTP / 1.1 SSL支持开头。

3.8.2. HTTP / 2与码头

对于HTTP / 2支持,Jetty需要附加的org.eclipse.jetty.http2:http2-server依赖关系。现在,根据您的部署,还需要选择其他依赖项:

  • org.eclipse.jetty:jetty-alpn-java-server 适用于在JDK9 +上运行的应用程序
  • org.eclipse.jetty:jetty-alpn-openjdk8-server 适用于在JDK8u252 +上运行的应用程序
  • org.eclipse.jetty:jetty-alpn-conscrypt-server和没有JDK要求的Conscrypt库

    3.8.3. HTTP / 2和Reactor Netty

    spring-boot-webflux-starter默认情况下,反应堆的Netty作为服务器使用。使用JDK 9或更高版本的JDK支持,可以将Reactor Netty配置为HTTP / 2。对于JDK 8环境,或为了获得最佳的运行时性能,此服务器还支持带有本机库的HTTP / 2。为此,您的应用程序需要具有其他依赖关系。
    Spring Boot管理io.netty:netty-tcnative-boringssl-static“超级罐”的版本,其中包含所有平台的本机库。开发人员可以选择使用分类器仅导入所需的依赖项(请参阅Netty官方文档)。

    3.8.4. 带有Undertow的HTTP / 2

    从Undertow 1.4.0+开始,在JDK8上没有任何其他要求就支持HTTP / 2。

    3.8.5. 支持服务器的HTTP / 2明文

    要启用具有明文支持的HTTP / 2,您需要将该server.http2.enabled属性设置为false,而是应用特定于您选择的服务器的定制程序:
    对于Tomcat,我们需要添加一个升级协议:
    1. @Bean
    2. public TomcatConnectorCustomizer connectorCustomizer() {
    3. return (connector) -> connector.addUpgradeProtocol(new Http2Protocol());
    4. }
    对于Jetty,我们需要向现有连接器添加连接工厂:
    1. @Bean
    2. public JettyServerCustomizer serverCustomizer() {
    3. return (server) -> {
    4. HttpConfiguration configuration = new HttpConfiguration();
    5. configuration.setSendServerVersion(false);
    6. Arrays.stream(server.getConnectors())
    7. .filter(connector -> connector instanceof ServerConnector)
    8. .map(ServerConnector.class::cast)
    9. .forEach(connector -> {
    10. connector.addConnectionFactory(new HTTP2CServerConnectionFactory(configuration));
    11. });
    12. };
    13. }
    对于Netty,我们需要添加h2c作为受支持的协议:
    1. @Bean
    2. public NettyServerCustomizer serverCustomizer() {
    3. return (server) -> server.protocol(HttpProtocol.H2C);
    4. }
    对于Undertow,我们需要启用HTTP2选项:
    1. @Bean
    2. public UndertowBuilderCustomizer builderCustomizer() {
    3. return (builder) -> {
    4. builder.setServerOption(ENABLE_HTTP2, true);
    5. };
    6. }

    3.9. 配置Web服务器

    通常,您应该首先考虑使用许多可用的配置键之一,并通过在您的application.properties(或application.yml,或环境等)中添加新条目来自定义Web服务器。请参阅“发现外部属性的内置选项”。该server.*命名空间是非常有用的在这里,它包括命名空间一样server.tomcat.*server.jetty.*和其他人,对服务器的特定功能。请参阅appendix-application-properties.html的列表。
    前面的部分已经介绍了许多常见的用例,例如压缩,SSL或HTTP / 2。但是,如果您的用例不存在配置密钥,则应查看WebServerFactoryCustomizer。您可以声明一个这样的组件,并访问与您选择的服务器相关的工厂:您应该为所选服务器(Tomcat,Jetty,Reactor Netty,Undertow)和所选Web堆栈(Servlet或Reactive)选择变体。
    以下示例适用于具有spring-boot-starter-web(Servlet堆栈)的Tomcat :
    1. @Component
    2. public class MyTomcatWebServerCustomizer
    3. implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    4. @Override
    5. public void customize(TomcatServletWebServerFactory factory) {
    6. // customize the factory here
    7. }
    8. }
    此外,Spring Boot还提供:
服务器 Servlet堆栈 反应堆
雄猫 TomcatServletWebServerFactory TomcatReactiveWebServerFactory
码头 JettyServletWebServerFactory JettyReactiveWebServerFactory
底拖 UndertowServletWebServerFactory UndertowReactiveWebServerFactory
反应堆 不适用 NettyReactiveWebServerFactory

一旦访问了WebServerFactory,就可以经常向其添加定制程序,以配置特定的部分,例如连接器,服务器资源或服务器本身-全部使用服务器特定的API。
最后,您还可以声明自己的WebServerFactory组件,该组件将覆盖Spring Boot提供的组件。在这种情况下,您不能再依赖server命名空间中的配置属性。

3.10. 将Servlet,过滤器或侦听器添加到应用程序

在一个servlet栈的应用,即用spring-boot-starter-web,有两种方法可以添加ServletFilterServletContextListener,和由Servlet API到您的应用程序支持的其他听众:

  • 使用Spring Bean添加Servlet,过滤器或侦听器
  • 使用类路径扫描添加Servlet,过滤器和侦听器

    3.10.1. 使用Spring Bean添加Servlet,过滤器或侦听器

    要添加ServletFilter或servlet*Listener使用的Spring bean,你必须提供一个@Bean它的定义。当您要注入配置或依赖项时,这样做非常有用。但是,您必须非常小心,以免引起过多其他bean的急切初始化,因为必须在应用程序生命周期的早期就将它们安装在容器中。(例如,让它们依赖于您的DataSource或JPA配置不是一个好主意。)您可以通过在第一次使用bean时而不是在初始化时进行延迟初始化来解决这些限制。
    在的情况下FiltersServlets,还可以通过添加添加映射和初始化参数FilterRegistrationBeanServletRegistrationBean代替或除了下面的部件。
如果dispatcherType在过滤器注册上未指定no ,REQUEST则使用。这与Servlet规范的默认调度程序类型一致。

像其他任何Spring bean一样,您可以定义Servlet过滤器bean的顺序。请确保检查“ spring-boot-features.html ”部分。

禁用Servlet或过滤器的注册

正如前面所述,任何ServletFilter豆与servlet容器自动注册。要禁用特定FilterServletbean的注册,请为其创建注册bean并将其标记为已禁用,如以下示例所示:

  1. @Bean
  2. public FilterRegistrationBean registration(MyFilter filter) {
  3. FilterRegistrationBean registration = new FilterRegistrationBean(filter);
  4. registration.setEnabled(false);
  5. return registration;
  6. }

3.10.2. 使用类路径扫描添加Servlet,过滤器和侦听器

@WebServlet,,@WebFilter和带@WebListener注释的类可以通过向注释一个@Configuration@ServletComponentScan并指定包含要注册的组件的包来自动向嵌入式servlet容器注册。默认情况下,@ServletComponentScan从带注释的类的包进行扫描。

3.11. 配置访问日志

可以通过它们各自的名称空间为Tomcat,Undertow和Jetty配置访问日志。
例如,以下设置使用自定义模式记录对Tomcat的访问。
物产
Yaml

  1. server.tomcat.basedir=my-tomcat
  2. server.tomcat.accesslog.enabled=true
  3. server.tomcat.accesslog.pattern=%t %a %r %s (%D ms)
日志的默认位置是logs相对于Tomcat基本目录的目录。默认情况下,该logs目录是一个临时目录,因此您可能需要修复Tomcat的基本目录或为日志使用绝对路径。在前面的示例中,日志my-tomcat/logs相对于应用程序的工作目录可用。

可以用类似的方式配置Undertow的访问日志,如以下示例所示:
物产
Yaml

  1. server.undertow.accesslog.enabled=true
  2. server.undertow.accesslog.pattern=%t %a %r %s (%D ms)

日志存储在logs相对于应用程序工作目录的目录中。您可以通过设置server.undertow.accesslog.dir属性来自定义此位置。
最后,Jetty的访问日志也可以配置如下:
物产
Yaml

  1. server.jetty.accesslog.enabled=true
  2. server.jetty.accesslog.filename=/var/log/jetty-access.log

默认情况下,日志重定向到System.err。有关更多详细信息,请参见Jetty文档

3.12. 在前端代理服务器后面运行

如果您的应用程序是在代理,负载均衡器之后或在云中运行的,则请求信息(例如主机,端口,方案等)可能会随之变化。您的应用程序可能正在运行10.10.10.10:8080,但是HTTP客户端只能看到example.org
RFC7239“转发标头”定义了ForwardedHTTP标头;代理可以使用此标头来提供有关原始请求的信息。您可以将应用程序配置为读取这些标头,并在创建链接并将其发送到HTTP 302响应,JSON文档或HTML页面中的客户端时自动使用该信息。还有一些非标准的标题,如:X-Forwarded-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-Ssl,和X-Forwarded-Prefix
如果代理添加常用X-Forwarded-ForX-Forwarded-Proto标题,设置server.forward-headers-strategyNATIVE足以支持这些。使用此选项,Web服务器本身就本身支持此功能。您可以查看他们的特定文档以了解特定行为。
如果这还不够,Spring框架将提供ForwardedHeaderFilter。通过将设置server.forward-headers-strategy为,可以将其注册为应用程序中的Servlet过滤器FRAMEWORK

如果您的应用程序在Cloud Foundry或Heroku中运行,则该server.forward-headers-strategy属性默认为NATIVE。在所有其他情况下,它默认为NONE

3.12.1. 自定义Tomcat的代理配置

如果使用Tomcat,则可以另外配置用于承载“转发的”信息的标头名称,如以下示例所示:
物产
Yaml

  1. server.tomcat.remoteip.remote-ip-header=x-your-remote-ip-header
  2. server.tomcat.remoteip.protocol-header=x-your-protocol-header

Tomcat还配置有一个默认正则表达式,该正则表达式与要信任的内部代理匹配。默认情况下,IP地址10/8192.168/16169.254/16并且127/8是值得信赖的。您可以通过在上添加一个条目来自定义阀门的配置application.properties,如以下示例所示:
物产
Yaml

  1. server.tomcat.remoteip.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
您可以通过将设置internal-proxies为空来信任所有代理(但在生产环境中不要这样做)。

您可以RemoteIpValve通过关闭自动开关(设置为set server.forward-headers-strategy=NONE)并在TomcatServletWebServerFactorybean中添加新的Valve实例来完全控制Tomcat的配置。

3.13. 使用Tomcat启用多个连接器

您可以在中添加,org.apache.catalina.connector.ConnectorTomcatServletWebServerFactory允许多个连接器,包括HTTP和HTTPS连接器,如以下示例所示:

  1. @Bean
  2. public ServletWebServerFactory servletContainer() {
  3. TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
  4. tomcat.addAdditionalTomcatConnectors(createSslConnector());
  5. return tomcat;
  6. }
  7. private Connector createSslConnector() {
  8. Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
  9. Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
  10. try {
  11. File keystore = new ClassPathResource("keystore").getFile();
  12. File truststore = new ClassPathResource("keystore").getFile();
  13. connector.setScheme("https");
  14. connector.setSecure(true);
  15. connector.setPort(8443);
  16. protocol.setSSLEnabled(true);
  17. protocol.setKeystoreFile(keystore.getAbsolutePath());
  18. protocol.setKeystorePass("changeit");
  19. protocol.setTruststoreFile(truststore.getAbsolutePath());
  20. protocol.setTruststorePass("changeit");
  21. protocol.setKeyAlias("apitester");
  22. return connector;
  23. }
  24. catch (IOException ex) {
  25. throw new IllegalStateException("can't access keystore: [" + keystore
  26. + "] or truststore: [" + truststore + "]", ex);
  27. }
  28. }

3.14. 使用Tomcat的LegacyCookieProcessor

默认情况下,Spring Boot使用的嵌入式Tomcat不支持Cookie格式的“版本0”,因此您可能会看到以下错误:
java.lang.IllegalArgumentException:Cookie值中存在无效字符[32]
如果可能的话,您应该考虑将代码更新为仅存储符合以后Cookie规范的值。但是,如果无法更改cookie的编写方式,则可以将Tomcat配置为使用LegacyCookieProcessor。要切换到LegacyCookieProcessor,请使用WebServerFactoryCustomizer添加了的Bean TomcatContextCustomizer,如以下示例所示:

  1. @Bean
  2. public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
  3. return (factory) -> factory
  4. .addContextCustomizers((context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
  5. }

3.15. 启用Tomcat的MBean注册表

默认情况下,嵌入式Tomcat的MBean注册表是禁用的。这样可以最大程度地减少Tomcat的内存占用。例如,如果要使用Tomcat的MBean,以便可以将它们用于通过Micrometer公开指标,则必须使用该server.tomcat.mbeanregistry.enabled属性,如以下示例所示:
物产
Yaml

  1. server.tomcat.mbeanregistry.enabled=true

3.16. 使用Undertow启用多个侦听器

向中添加一个UndertowBuilderCustomizerUndertowServletWebServerFactory并向中添加一个侦听器Builder,如以下示例所示:

  1. @Bean
  2. public UndertowServletWebServerFactory servletWebServerFactory() {
  3. UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
  4. factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
  5. @Override
  6. public void customize(Builder builder) {
  7. builder.addHttpListener(8080, "0.0.0.0");
  8. }
  9. });
  10. return factory;
  11. }

3.17. 使用@ServerEndpoint创建WebSocket端点

如果要在使用@ServerEndpoint嵌入式容器的Spring Boot应用程序中使用,必须声明一个single ServerEndpointExporter @Bean,如以下示例所示:

  1. @Bean
  2. public ServerEndpointExporter serverEndpointExporter() {
  3. return new ServerEndpointExporter();
  4. }

前面示例中显示的bean将所有带@ServerEndpoint注释的bean注册到基础WebSocket容器中。当部署到独立的servlet容器时,此角色由servlet容器初始化程序执行,并且ServerEndpointExporter不需要bean。