嵌入式 Web 服务器

每个 Spring Boot Web 应用程序都包含一个嵌入式 Web 服务器。 这个特性引发了许多问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。 本节将回答这些问题。

使用其他 Web 服务器

许多 Spring Boot starter 包含默认的嵌入式容器。

  • 对于 servlet 栈应用程序,spring-boot-starter-web 通过包含 spring-boot-starter-tomcat 来包含 Tomcat,但你可以使用 spring-boot-starter-jettyspring-boot-starter-undertow 代替。

  • 对于响应式栈应用程序,spring-boot-starter-webflux 通过包含 spring-boot-starter-reactor-netty 来包含 Reactor Netty,但你可以使用 spring-boot-starter-tomcatspring-boot-starter-jettyspring-boot-starter-undertow 代替。

当切换到不同的 HTTP 服务器时,你需要将默认依赖项替换为你需要的依赖项。 为了帮助完成这个过程,Spring Boot 为每个支持的 HTTP 服务器提供了单独的 starter。

以下 Maven 示例展示了如何为 Spring MVC 排除 Tomcat 并包含 Jetty:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<exclusions>
		<!-- Exclude the Tomcat dependency -->
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

以下 Gradle 示例配置了必要的依赖项和 模块替换,以在 Spring WebFlux 中使用 Undertow 代替 Reactor Netty:

dependencies {
	implementation "org.springframework.boot:spring-boot-starter-undertow"
	implementation "org.springframework.boot:spring-boot-starter-webflux"
	modules {
		module("org.springframework.boot:spring-boot-starter-reactor-netty") {
			replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Reactor Netty")
		}
	}
}

注意:spring-boot-starter-reactor-netty 是使用 WebClient 类所必需的,因此即使你需要包含不同的 HTTP 服务器,也可能需要保留对 Netty 的依赖。

禁用 Web 服务器

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

  • Properties

  • YAML

spring.main.web-application-type=none
spring:
  main:
    web-application-type: "none"

更改 HTTP 端口

在独立应用程序中,主 HTTP 端口默认为 8080,但可以使用 server.port 设置(例如,在 application.properties 中或作为系统属性)。 由于 Environment 值的宽松绑定,你也可以使用 SERVER_PORT(例如,作为操作系统环境变量)。

要完全关闭 HTTP 端点但仍创建 WebApplicationContext,请使用 server.port=-1(这样做有时对测试很有用)。

有关更多详细信息,请参阅"Spring Boot 特性"部分中的 自定义嵌入式 Servlet 容器,或 ServerProperties 类。

使用随机未分配的 HTTP 端口

要扫描空闲端口(使用操作系统原生功能防止冲突),请使用 server.port=0

在运行时发现 HTTP 端口

你可以从日志输出或通过 WebServerApplicationContextWebServer 访问服务器运行的端口。 获取该端口并确保它已初始化的最佳方法是添加类型为 ApplicationListener<WebServerInitializedEvent>@Bean,并在事件发布时从事件中提取容器。

使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) 的测试也可以通过使用 @LocalServerPort 注解将实际端口注入到字段中,如下例所示:

  • Java

  • Kotlin

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyWebIntegrationTests {

	@LocalServerPort
	int port;

	// ...

}
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.server.LocalServerPort

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyWebIntegrationTests {

	@LocalServerPort
	var port = 0

	// ...

}

@LocalServerPort@Value("${local.server.port}") 的元注解。 不要在常规应用程序中尝试注入端口。 正如我们刚才看到的,该值仅在容器初始化后才设置。 与测试相反,应用程序代码回调会提前处理(在实际值可用之前)。

启用 HTTP 响应压缩

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

  • Properties

  • YAML

server.compression.enabled=true
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 属性来配置此行为。

配置 SSL

可以通过设置各种 server.ssl.* 属性来声明式地配置 SSL,通常在 application.propertiesapplication.yaml 中。 有关所有支持的属性的详细信息,请参阅 Ssl

以下示例展示了使用 Java KeyStore 文件设置 SSL 属性:

  • Properties

  • YAML

server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret
server:
  port: 8443
  ssl:
    key-store: "classpath:keystore.jks"
    key-store-password: "secret"
    key-password: "another-secret"

使用上述示例的配置意味着应用程序不再支持端口 8080 上的普通 HTTP 连接器。 Spring Boot 不支持通过 application.properties 同时配置 HTTP 连接器和 HTTPS 连接器。 如果你想要同时拥有两者,你需要以编程方式配置其中一个。 我们建议使用 application.properties 配置 HTTPS,因为 HTTP 连接器是两者中更容易以编程方式配置的。

使用 PEM 编码的文件

你可以使用 PEM 编码的文件代替 Java KeyStore 文件。 你应该尽可能使用 PKCS#8 密钥文件。 PEM 编码的 PKCS#8 密钥文件以 -----BEGIN PRIVATE KEY----------BEGIN ENCRYPTED PRIVATE KEY----- 开头。

如果你有其他格式的文件,例如 PKCS#1(-----BEGIN RSA PRIVATE KEY-----)或 SEC 1(-----BEGIN EC PRIVATE KEY-----),你可以使用 OpenSSL 将它们转换为 PKCS#8:

openssl pkcs8 -topk8 -nocrypt -in <input file> -out <output file>

以下示例展示了使用 PEM 编码的证书和私钥文件设置 SSL 属性:

  • Properties

  • YAML

server.port=8443
server.ssl.certificate=classpath:my-cert.crt
server.ssl.certificate-private-key=classpath:my-cert.key
server.ssl.trust-certificate=classpath:ca-cert.crt
server:
  port: 8443
  ssl:
    certificate: "classpath:my-cert.crt"
    certificate-private-key: "classpath:my-cert.key"
    trust-certificate: "classpath:ca-cert.crt"

使用 SSL 包

或者,SSL 信任材料可以在 SSL 包中配置,并应用于 Web 服务器,如下例所示:

  • Properties

  • YAML

server.port=8443
server.ssl.bundle=example
server:
  port: 8443
  ssl:
    bundle: "example"

server.ssl.bundle 属性不能与 server.ssl 下的离散 Java KeyStore 或 PEM 属性选项组合使用。

使用包时,server.ssl.ciphersserver.ssl.enabled-protocolsserver.ssl.protocol 属性也会被忽略。 这些属性应该使用 spring.ssl.bundle.<type>.<name>.options 属性定义。

配置服务器名称指示

Tomcat、Netty 和 Undertow 可以配置为对各个主机名使用唯一的 SSL 信任材料,以支持服务器名称指示(SNI)。 Jetty 不支持 SNI 配置,但如果向 Jetty 提供多个证书,它可以 自动设置 SNI

假设已配置了名为 webweb-alt1web-alt2SSL 包,以下配置可用于将每个包分配给嵌入式 Web 服务器提供的主机名:

  • Properties

  • YAML

server.port=8443
server.ssl.bundle=web
server.ssl.server-name-bundles[0].server-name=alt1.example.com
server.ssl.server-name-bundles[0].bundle=web-alt1
server.ssl.server-name-bundles[1].server-name=alt2.example.com
server.ssl.server-name-bundles[1].bundle=web-alt2
server:
  port: 8443
  ssl:
    bundle: "web"
    server-name-bundles:
      - server-name: "alt1.example.com"
        bundle: "web-alt1"
      - server-name: "alt2.example.com"
        bundle: "web-alt2"

使用 server.ssl.bundle 指定的包将用于默认主机,以及任何不支持 SNI 的客户端。 如果配置了任何 server.ssl.server-name-bundles,则必须配置此默认包。

配置 HTTP/2

你可以使用 server.http2.enabled 配置属性在 Spring Boot 应用程序中启用 HTTP/2 支持。 同时支持 h2(基于 TLS 的 HTTP/2)和 h2c(基于 TCP 的 HTTP/2)。 要使用 h2,还必须启用 SSL。 当未启用 SSL 时,将使用 h2c。 例如,当你的应用程序 在代理服务器后面运行时,你可能想要使用 h2c,该代理服务器正在执行 TLS 终止。

使用 Tomcat 的 HTTP/2

Spring Boot 默认附带 Tomcat 10.1.x,它开箱即用地支持 h2ch2。 或者,如果主机操作系统上安装了 libtcnative 库及其依赖项,你可以使用它来支持 h2

如果尚未提供,必须使库目录可用于 JVM 库路径。 你可以使用 JVM 参数(如 -Djava.library.path=/usr/local/opt/tomcat-native/lib)来执行此操作。 有关更多信息,请参阅 官方 Tomcat 文档

使用 Jetty 的 HTTP/2

对于 HTTP/2 支持,Jetty 需要额外的 org.eclipse.jetty.http2:jetty-http2-server 依赖项。 要使用 h2c,不需要其他依赖项。 要使用 h2,你还需要根据你的部署选择以下依赖项之一:

  • org.eclipse.jetty:jetty-alpn-java-server 使用 JDK 内置支持

  • org.eclipse.jetty:jetty-alpn-conscrypt-serverConscrypt 库

使用 Reactor Netty 的 HTTP/2

spring-boot-webflux-starter 默认使用 Reactor Netty 作为服务器。 Reactor Netty 开箱即用地支持 h2ch2。 为了获得最佳运行时性能,此服务器还支持使用本地库的 h2。 要启用此功能,你的应用程序需要额外的依赖项。

Spring Boot 管理 io.netty:netty-tcnative-boringssl-static "uber jar" 的版本,其中包含所有平台的本地库。 开发人员可以选择使用分类器仅导入所需的依赖项(请参阅 Netty 官方文档)。

使用 Undertow 的 HTTP/2

Undertow 开箱即用地支持 h2ch2

配置 Web 服务器

通常,你应该首先考虑使用许多可用的配置键,并通过在 application.propertiesapplication.yaml 文件中添加新条目来自定义 Web 服务器。 请参阅 发现外部属性的内置选项)。 server.* 命名空间在这里非常有用,它包括 server.tomcat.*server.jetty.* 等命名空间,用于服务器特定功能。 请参阅 常用应用程序属性 列表。

前面的部分已经涵盖了许多常见用例,如压缩、SSL 或 HTTP/2。 但是,如果你的用例没有配置键,你应该查看 WebServerFactoryCustomizer。 你可以声明这样的组件并访问与你的选择相关的服务器工厂:你应该为所选服务器(Tomcat、Jetty、Reactor Netty、Undertow)和所选 Web 栈(servlet 或响应式)选择变体。

以下示例适用于使用 spring-boot-starter-web(servlet 栈)的 Tomcat:

  • Java

  • Kotlin

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

	@Override
	public void customize(TomcatServletWebServerFactory factory) {
		// customize the factory here
	}

}
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component

@Component
class MyTomcatWebServerCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory?> {

	override fun customize(factory: TomcatServletWebServerFactory?) {
		// customize the factory here
	}

}

注意:Spring Boot 在内部使用该基础设施来自动配置服务器。 自动配置的 WebServerFactoryCustomizer bean 的优先级为 0,将在任何用户定义的自定义器之前处理,除非它有明确指定其他优先级的顺序。

一旦你使用自定义器访问了 WebServerFactory,你就可以使用它来配置特定部分,如连接器、服务器资源或服务器本身 - 所有这些都使用服务器特定的 API。

此外,Spring Boot 提供:

服务器 Servlet 栈 响应式栈

Tomcat

TomcatServletWebServerFactory

TomcatReactiveWebServerFactory

Jetty

JettyServletWebServerFactory

JettyReactiveWebServerFactory

Undertow

UndertowServletWebServerFactory

UndertowReactiveWebServerFactory

Reactor

N/A

NettyReactiveWebServerFactory

作为最后的手段,你也可以声明自己的 WebServerFactory bean,它将覆盖 Spring Boot 提供的那个。 当你这样做时,自动配置的自定义器仍然会应用于你的自定义工厂,所以要谨慎使用该选项。

向应用程序添加 Servlet、Filter 或 Listener

在 servlet 栈应用程序中,即使用 spring-boot-starter-web 的应用程序,有两种方法可以向应用程序添加 ServletFilterServletContextListener 以及 Servlet API 支持的其他监听器:

使用 Spring Bean 添加 Servlet、Filter 或 Listener

要使用 Spring bean 添加 ServletFilter 或 servlet *Listener,你必须为其提供 @Bean 定义。 当你想要注入配置或依赖项时,这非常有用。 但是,你必须非常小心,不要导致太多其他 bean 的急切初始化,因为它们必须在应用程序生命周期的早期安装在容器中。 (例如,让它们依赖于你的 DataSource 或 JPA 配置不是一个好主意。) 你可以通过在首次使用时延迟初始化 bean 而不是在初始化时初始化来解决这些限制。

对于过滤器和 servlet,你还可以通过添加 FilterRegistrationBeanServletRegistrationBean 来添加映射和初始化参数,而不是或除了底层组件之外。

如果在过滤器注册上未指定 dispatcherType,则使用 REQUEST。 这与 servlet 规范的默认调度器类型一致。

像任何其他 Spring bean 一样,你可以定义 servlet 过滤器 bean 的顺序;请确保查看 将 Servlets、Filters 和 Listeners 注册为 Spring Bean 部分。

禁用 Servlet 或 Filter 的注册

前面所述,任何 ServletFilter bean 都会自动注册到 servlet 容器中。 要禁用特定 FilterServlet bean 的注册,请为其创建注册 bean 并将其标记为禁用,如下例所示:

  • Java

  • Kotlin

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {

	@Bean
	public FilterRegistrationBean<MyFilter> registration(MyFilter filter) {
		FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
		registration.setEnabled(false);
		return registration;
	}

}
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {

	@Bean
	fun registration(filter: MyFilter): FilterRegistrationBean<MyFilter> {
		val registration = FilterRegistrationBean(filter)
		registration.isEnabled = false
		return registration
	}

}

使用类路径扫描添加 Servlet、Filter 和 Listener

通过使用 @ServletComponentScan 注解 @Configuration 类并指定包含要注册的组件的包,可以自动将 @WebServlet@WebFilter@WebListener 注解的类注册到嵌入式 servlet 容器中。 默认情况下,@ServletComponentScan 从带注解的类的包开始扫描。

配置访问日志

可以通过各自的命名空间为 Tomcat、Undertow 和 Jetty 配置访问日志。

例如,以下设置使用 自定义模式在 Tomcat 上记录访问。

  • Properties

  • YAML

server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a %r %s (%D microseconds)
server:
  tomcat:
    basedir: "my-tomcat"
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D microseconds)"

注意:日志的默认位置是相对于 Tomcat 基础目录的 logs 目录。 默认情况下,logs 目录是一个临时目录,因此你可能想要修复 Tomcat 的基础目录或使用绝对路径作为日志。 在前面的示例中,日志在应用程序工作目录的 my-tomcat/logs 中可用。

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

  • Properties

  • YAML

server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a %r %s (%D milliseconds)
server.undertow.options.server.record-request-start-time=true
server:
  undertow:
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D milliseconds)"
    options:
      server:
        record-request-start-time: true

注意,除了启用访问日志和配置其模式外,还启用了记录请求开始时间。 当在访问日志模式中包含响应时间(%D)时,这是必需的。 日志存储在应用程序工作目录的 logs 目录中。 你可以通过设置 server.undertow.accesslog.dir 属性来自定义此位置。

最后,也可以如下配置 Jetty 的访问日志:

  • Properties

  • YAML

server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=/var/log/jetty-access.log
server:
  jetty:
    accesslog:
      enabled: true
      filename: "/var/log/jetty-access.log"

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

在代理服务器后面运行

如果你的应用程序在代理、负载均衡器或云中运行,请求信息(如主机、端口、方案等)可能会在传输过程中发生变化。 你的应用程序可能在 10.10.10.10:8080 上运行,但 HTTP 客户端应该只看到 example.org

RFC7239 "Forwarded Headers" 定义了 Forwarded HTTP 头;代理可以使用此头提供有关原始请求的信息。 你可以配置应用程序读取这些头,并在创建链接并将其发送给 HTTP 302 响应、JSON 文档或 HTML 页面中的客户端时自动使用该信息。 还有一些非标准头,如 X-Forwarded-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-SslX-Forwarded-Prefix

如果代理添加了常用的 X-Forwarded-ForX-Forwarded-Proto 头,将 server.forward-headers-strategy 设置为 NATIVE 就足以支持这些。 使用此选项,Web 服务器本身原生支持此功能;你可以查看其特定文档以了解具体行为。

如果这还不够,Spring Framework 为 servlet 栈提供了 ForwardedHeaderFilter,为响应式栈提供了 ForwardedHeaderTransformer。 你可以通过将 server.forward-headers-strategy 设置为 FRAMEWORK 在应用程序中使用它们。

提示:如果你使用 Tomcat 并在代理处终止 SSL,应该将 server.tomcat.redirect-context-root 设置为 false。 这允许在执行任何重定向之前遵守 X-Forwarded-Proto 头。

注意:如果你的应用程序在 支持的云平台中运行,server.forward-headers-strategy 属性默认为 NATIVE。 在所有其他情况下,它默认为 NONE

自定义 Tomcat 的代理配置

如果你使用 Tomcat,你还可以配置用于携带"forwarded"信息的头的名称,如下例所示:

  • Properties

  • YAML

server.tomcat.remoteip.remote-ip-header=x-your-remote-ip-header
server.tomcat.remoteip.protocol-header=x-your-protocol-header
server:
  tomcat:
    remoteip:
      remote-ip-header: "x-your-remote-ip-header"
      protocol-header: "x-your-protocol-header"

Tomcat 还配置了一个正则表达式,用于匹配要信任的内部代理。 有关其默认值,请参阅附录中的 server.tomcat.remoteip.internal-proxies 条目。 你可以通过向 application.properties 添加条目来自定义阀的配置,如下例所示:

  • Properties

  • YAML

server.tomcat.remoteip.internal-proxies=192\.168\.\d{1,3}\.\d{1,3}
server:
  tomcat:
    remoteip:
      internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}"

注意:你可以通过将 internal-proxies 设置为空来信任所有代理(但在生产环境中不要这样做)。

你可以通过关闭自动配置(为此,设置 server.forward-headers-strategy=NONE)并使用 WebServerFactoryCustomizer bean 添加新的阀实例来完全控制 Tomcat 的 RemoteIpValve 的配置。

在 Tomcat 中启用多个连接器

你可以向 TomcatServletWebServerFactory 添加 Connector,这可以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如下例所示:

  • Java

  • Kotlin

import org.apache.catalina.connector.Connector;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyTomcatConfiguration {

	@Bean
	public WebServerFactoryCustomizer<TomcatServletWebServerFactory> connectorCustomizer() {
		return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createConnector());
	}

	private Connector createConnector() {
		Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
		connector.setPort(8081);
		return connector;
	}

}
import org.apache.catalina.connector.Connector
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyTomcatConfiguration {

	@Bean
	fun connectorCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
		return WebServerFactoryCustomizer { tomcat: TomcatServletWebServerFactory ->
			tomcat.addAdditionalTomcatConnectors(
				createConnector()
			)
		}
	}

	private fun createConnector(): Connector {
		val connector = Connector("org.apache.coyote.http11.Http11NioProtocol")
		connector.port = 8081
		return connector
	}

}

启用 Tomcat 的 MBean 注册表

嵌入式 Tomcat 的 MBean 注册表默认是禁用的。 这最小化了 Tomcat 的内存占用。 如果你想使用 Tomcat 的 MBean,例如让 Micrometer 可以使用它们来暴露指标,你必须使用 server.tomcat.mbeanregistry.enabled 属性来执行此操作,如下例所示:

  • Properties

  • YAML

server.tomcat.mbeanregistry.enabled=true
server:
  tomcat:
    mbeanregistry:
      enabled: true

在 Undertow 中启用多个监听器

UndertowServletWebServerFactory 添加 UndertowBuilderCustomizer,并向 io.undertow.Undertow.Builder 添加监听器,如下例所示:

  • Java

  • Kotlin

import io.undertow.Undertow.Builder;

import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyUndertowConfiguration {

	@Bean
	public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowListenerCustomizer() {
		return (factory) -> factory.addBuilderCustomizers(this::addHttpListener);
	}

	private Builder addHttpListener(Builder builder) {
		return builder.addHttpListener(8080, "0.0.0.0");
	}

}
import io.undertow.Undertow
import org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyUndertowConfiguration {

	@Bean
	fun undertowListenerCustomizer(): WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
		return WebServerFactoryCustomizer { factory: UndertowServletWebServerFactory ->
			factory.addBuilderCustomizers(
				UndertowBuilderCustomizer { builder: Undertow.Builder -> addHttpListener(builder) })
		}
	}

	private fun addHttpListener(builder: Undertow.Builder): Undertow.Builder {
		return builder.addHttpListener(8080, "0.0.0.0")
	}

}

使用 @ServerEndpoint 创建 WebSocket 端点

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

  • Java

  • Kotlin

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration(proxyBeanMethods = false)
public class MyWebSocketConfiguration {

	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}

}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.socket.server.standard.ServerEndpointExporter

@Configuration(proxyBeanMethods = false)
class MyWebSocketConfiguration {

	@Bean
	fun serverEndpointExporter(): ServerEndpointExporter {
		return ServerEndpointExporter()
	}

}

前面示例中显示的 bean 将任何 @ServerEndpoint 注解的 bean 注册到底层 WebSocket 容器。 当部署到独立的 servlet 容器时,此角色由 servlet 容器初始化器执行,不需要 ServerEndpointExporter bean。