嵌入式 Web 服务器
每个 Spring Boot Web 应用程序都包含一个内嵌的 Web 服务器。这一特性引发了许多“如何做”的问题,包括如何更改内嵌服务器以及如何配置内嵌服务器。本节将回答这些问题。
使用其他 Web 服务器
许多 Spring Boot starter 包含默认的嵌入式容器。
-
对于 Servlet 栈应用程序,
spring-boot-starter-web通过包含spring-boot-starter-tomcat来引入 Tomcat,但你也可以改用spring-boot-starter-jetty或spring-boot-starter-undertow。 -
对于响应式栈应用程序,
spring-boot-starter-webflux通过包含spring-boot-starter-reactor-netty来引入 Reactor Netty,但你也可以改用spring-boot-starter-tomcat、spring-boot-starter-jetty或spring-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 示例配置了必要的依赖项和一个 模块替换,以使用 Undertow 替代 Reactor Netty 作为 Spring WebFlux 的底层实现:
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")
}
}
}
使用 WebClient 类需要 spring-boot-starter-reactor-netty,因此即使你需要引入其他 HTTP 服务器,也可能仍需保留对 Netty 的依赖。
禁用 Web 服务器
如果你的 classpath 中包含启动 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 功能”部分中的 Customizing Embedded Servlet Containers,或 ServerProperties 类。
使用随机未分配的 HTTP 端口
要扫描一个空闲端口(使用操作系统原生机制以避免冲突),请使用 server.port=0。
在运行时发现 HTTP 端口
你可以从日志输出中获取服务器运行的端口,也可以通过 WebServerApplicationContext 获取其关联的 WebServer 来获取该端口。要确保该端口已被初始化并获取它,最佳方式是添加一个类型为 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 响应压缩
HTTP 响应压缩由 Jetty、Tomcat、Reactor Netty 和 Undertow 支持。可以在 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.properties 或 application.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 bundle 中,并如本例所示应用到 Web 服务器:
- Properties
- YAML
server.port=8443
server.ssl.bundle=example
server:
port: 8443
ssl:
bundle: "example"
server.ssl.bundle 属性不能与 server.ssl 下的独立 Java KeyStore 或 PEM 属性选项结合使用。
使用 bundle 时,server.ssl.ciphers、server.ssl.enabled-protocols 和 server.ssl.protocol 属性也会被忽略。这些属性应改用 spring.ssl.bundle.<type>.<name>.options 属性来定义。
配置服务器名称指示(SNI)
Tomcat、Netty 和 Undertow 可以配置为针对各个主机名使用独立的 SSL 信任材料,以支持服务器名称指示(SNI)。Jetty 不支持 SNI 配置,但如果向 Jetty 提供了多个证书,它可以自动设置 SNI。
假设已配置了名为 web、web-alt1 和 web-alt2 的 SSL 证书包,以下配置可用于将每个证书包分配给嵌入式 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 指定的 bundle 将用于默认主机,以及任何支持 SNI 的客户端。如果配置了任何 server.ssl.server-name-bundles,则必须配置此默认 bundle。
配置 HTTP/2
你可以通过 server.http2.enabled 配置属性在 Spring Boot 应用中启用 HTTP/2 支持。h2(基于 TLS 的 HTTP/2)和 h2c(基于 TCP 的 HTTP/2)均受支持。要使用 h2,还必须启用 SSL。当未启用 SSL 时,将使用 h2c。例如,当你的应用运行在执行 TLS 终止的代理服务器之后时,你可能希望使用 h2c。
HTTP/2 与 Tomcat
Spring Boot 默认自带 Tomcat 10.1.x,它开箱即用地支持 h2c 和 h2。或者,如果在主机操作系统上安装了 libtcnative 库及其依赖项,你也可以使用 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-server和 Conscrypt library
使用 Reactor Netty 配置 HTTP/2
spring-boot-webflux-starter 默认使用 Reactor Netty 作为服务器。Reactor Netty 开箱即用地支持 h2c 和 h2。为了获得最佳的运行时性能,该服务器还通过原生库支持 h2。要启用此功能,您的应用程序需要添加一个额外的依赖项。
Spring Boot 管理 io.netty:netty-tcnative-boringssl-static “uber jar” 的版本,该 jar 包含了所有平台的本地库。开发者可以选择使用 classifier 仅导入所需的依赖(参见 Netty 官方文档)。
使用 Undertow 的 HTTP/2
Undertow 开箱即支持 h2c 和 h2。
配置 Web 服务器
通常,你应该首先考虑使用众多可用的配置键,并通过在 application.properties 或 application.yaml 文件中添加新条目来自定义你的 Web 服务器。参见 Discover Built-in Options for External Properties。这里的 server.* 命名空间非常有用,它包含诸如 server.tomcat.*、server.jetty.* 等命名空间,用于服务器特定的功能。参见 Common Application Properties 列表。
前面的章节已经涵盖了诸多常见用例,例如压缩、SSL 或 HTTP/2。然而,如果你的用例没有对应的配置键,那么你应该查看 WebServerFactoryCustomizer。你可以声明这样一个组件,并获得与你所选服务器相关的服务器工厂:你需要根据所选的 Server(Tomcat、Jetty、Reactor Netty、Undertow)和所选的 Web 栈(Servlet 或 Reactive)来选择相应的变体。
下面的示例适用于使用 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 还提供了:
| Server | Servlet stack | Reactive stack |
|---|---|---|
| Tomcat | TomcatServletWebServerFactory | TomcatReactiveWebServerFactory |
| Jetty | JettyServletWebServerFactory | JettyReactiveWebServerFactory |
| Undertow | UndertowServletWebServerFactory | UndertowReactiveWebServerFactory |
| Reactor | N/A | NettyReactiveWebServerFactory |
作为最后的手段,你也可以声明自己的 WebServerFactory bean,这将覆盖 Spring Boot 提供的默认实现。当你这样做时,自动配置的定制器(customizers)仍然会应用到你自定义的工厂上,因此请谨慎使用该选项。
向应用程序中添加 Servlet、Filter 或 Listener
在基于 Servlet 栈的应用程序中(即使用 spring-boot-starter-web 时),有两种方式可以将 Servlet、Filter、ServletContextListener 以及其他 Servlet API 支持的监听器添加到你的应用程序中:
通过使用 Spring Bean 添加 Servlet、Filter 或 Listener
要通过 Spring Bean 的方式添加 Servlet、Filter 或 servlet *Listener,你必须为其提供一个 @Bean 定义。当你希望注入配置或依赖项时,这种方式非常有用。然而,你必须格外小心,避免它们导致过多其他 Bean 的急切初始化,因为这些组件必须在应用程序生命周期的早期阶段就安装到容器中。(例如,让它们依赖于你的 DataSource 或 JPA 配置并不是一个好主意。)你可以通过在首次使用时才初始化这些 Bean(而不是在启动时初始化)来规避此类限制。
对于过滤器(filters)和 Servlet,除了直接使用底层组件外,还可以通过添加 FilterRegistrationBean 或 ServletRegistrationBean 来添加映射(mappings)和初始化参数(init parameters)。你也可以使用 @ServletRegistration 和 @FilterRegistration 作为基于注解的替代方案,以取代 ServletRegistrationBean 和 FilterRegistrationBean。
如果在过滤器注册中未指定 dispatcherType,则使用 REQUEST。这与 Servlet 规范的默认 dispatcher 类型一致。
与其他 Spring Bean 一样,你可以定义 Servlet 过滤器 Bean 的顺序;请务必查看 将 Servlet、过滤器和监听器注册为 Spring Bean 部分。
禁用 Servlet 或 Filter 的注册
- 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
使用 @WebServlet、@WebFilter 和 @WebListener 注解的类可以通过在 @Configuration 类上添加 @ServletComponentScan 注解并指定包含要注册组件的包,从而自动注册到内嵌的 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-Host、X-Forwarded-Port、X-Forwarded-Proto、X-Forwarded-Ssl 和 X-Forwarded-Prefix。
如果代理添加了常用的 X-Forwarded-For 和 X-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,还可以额外配置用于携带“转发”信息的 Header 名称,如下例所示:
- 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 中添加一个条目来自定义该 Valve 的配置,如下例所示:
- 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 添加一个新的 Valve 实例,从而完全控制 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 Registry
Embedded 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 容器初始化器(servlet container initializer)执行,此时不需要 ServerEndpointExporter bean。