嵌入式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示例配置了必要的依賴項和模組替換(module replacement),以便在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")
		}
	}
}
使用WebClient類需要spring-boot-starter-reactor-netty,因此即使你需要包含不同的HTTP伺服器,也可能需要保留對Netty的依賴。

停用Web伺服器

如果你的類路徑包含啟動Web伺服器所需的元件,Spring Boot將自動啟動它。要停用此行為,請在application.properties中配置WebApplicationType,如以下示例所示

  • 屬性檔案 (Properties)

  • YAML檔案 (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埠

你可以從日誌輸出或透過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響應壓縮

Jetty、Tomcat、Reactor Netty和Undertow都支援HTTP響應壓縮。可以在application.properties中啟用,如下所示

  • 屬性檔案 (Properties)

  • YAML檔案 (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檔案 (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檔案 (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 Bundle

或者,可以在SSL bundle中配置SSL信任材料,並將其應用於web伺服器,如下例所示

  • 屬性檔案 (Properties)

  • YAML檔案 (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.ciphersserver.ssl.enabled-protocolsserver.ssl.protocol屬性也會被忽略。這些屬性應改為使用spring.ssl.bundle.<type>.<name>.options屬性定義。

配置伺服器名稱指示 (SNI)

可以配置Tomcat、Netty和Undertow為不同的主機名使用獨特的SSL信任材料以支援伺服器名稱指示(SNI)。Jetty不支援SNI配置,但如果提供多個證書,Jetty可以自動設定SNI

假設已配置了名為webweb-alt1web-alt2SSL bundle,可以使用以下配置將每個bundle分配給嵌入式web伺服器提供服務的主機名

  • 屬性檔案 (Properties)

  • YAML檔案 (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

使用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" 的版本,該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或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,將在任何使用者定義的customizer之前處理,除非它有明確指定的排序。

一旦使用customizer獲得了對WebServerFactory的訪問權,你就可以使用它來配置特定部分,如聯結器、伺服器資源或伺服器本身——所有這些都使用伺服器特定的API。

此外,Spring Boot提供

伺服器 Servlet棧 響應式棧

Tomcat

TomcatServletWebServerFactory

TomcatReactiveWebServerFactory

Jetty

JettyServletWebServerFactory

JettyReactiveWebServerFactory

Undertow

UndertowServletWebServerFactory

UndertowReactiveWebServerFactory

Reactor

不適用 (N/A)

NettyReactiveWebServerFactory

作為最後的手段,你也可以宣告自己的WebServerFactory bean,這將覆蓋Spring Boot提供的bean。這樣做時,自動配置的customizer仍然會應用到你的自定義工廠上,因此請謹慎使用此選項。

嚮應用新增Servlet、Filter或Listener

在servlet棧應用中,即使用spring-boot-starter-web時,有兩種方法可以將ServletFilterServletContextListener以及Servlet API支援的其他listener新增到應用中

使用Spring Bean新增Servlet、Filter或Listener

要使用Spring bean新增ServletFilter或servlet *Listener,必須為其提供@Bean定義。這在你想要注入配置或依賴項時非常有用。但是,你必須非常小心,不要導致太多其他bean的提前初始化,因為它們必須在應用生命週期的早期安裝到容器中。(例如,讓它們依賴於你的DataSource或JPA配置不是一個好主意。)你可以透過在首次使用時而不是在初始化時懶惰地初始化bean來規避此類限制。

對於filter和servlet,你還可以透過新增FilterRegistrationBeanServletRegistrationBean來新增對映和初始化引數,而不是或除了底層元件之外。

如果在filter註冊中未指定dispatcherType,則使用REQUEST。這與servlet規範的預設dispatcher型別一致。

與任何其他Spring bean一樣,你可以定義servlet filter bean的順序;請務必檢視將Servlet、Filter和Listener註冊為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檔案 (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檔案 (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檔案 (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,還可以配置用於承載“轉發”資訊的頭名稱,如以下示例所示

  • 屬性檔案 (Properties)

  • YAML檔案 (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中新增條目來自定義valva的配置,如以下示例所示

  • 屬性檔案 (Properties)

  • YAML檔案 (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 啟用多個聯結器

您可以將一個 Connector 新增到 TomcatServletWebServerFactory 中,這允許使用多個聯結器,包括 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檔案 (YAML)

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

使用 Undertow 啟用多個監聽器

UndertowBuilderCustomizer 新增到 UndertowServletWebServerFactory 中,並將一個監聽器新增到 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。