註解控制器

應用程式可以使用帶有 @Controller 註解的類別來處理來自客户端的訊息。此類類別可以宣告 @MessageMapping@SubscribeMapping@ExceptionHandler 方法,如下列主題所述

@MessageMapping

您可以使用 @MessageMapping 來註解根據目的地路由訊息的方法。它在方法層級和型別層級都受到支援。在型別層級,@MessageMapping 用於表達控制器中所有方法共用的對應。

預設情況下,對應值是 Ant 樣式的路徑模式(例如 /thing*/thing/**),包括對範本變數的支援(例如,/thing/{id})。這些值可以透過 @DestinationVariable 方法引數來參考。應用程式也可以切換到以點分隔的目的地慣例來進行對應,如點作為分隔符號中所述。

支援的方法引數

下表描述了方法引數

方法引數 描述

Message

用於存取完整訊息。

MessageHeaders

用於存取 Message 內的標頭。

MessageHeaderAccessorSimpMessageHeaderAccessorStompHeaderAccessor

用於透過型別化的存取器方法存取標頭。

@Payload

用於存取訊息的酬載,由組態的 MessageConverter 轉換(例如,從 JSON 轉換)。

由於預設情況下,如果沒有其他引數匹配,則會假定存在此註解,因此不需要存在此註解。

您可以使用 @jakarta.validation.Valid 或 Spring 的 @Validated 註解酬載引數,以使酬載引數自動驗證。

@Header

用於存取特定的標頭值,並在必要時使用 org.springframework.core.convert.converter.Converter 進行型別轉換。

@Headers

用於存取訊息中的所有標頭。此引數必須可指派給 java.util.Map

@DestinationVariable

用於存取從訊息目的地擷取的範本變數。值會根據需要轉換為宣告的方法引數型別。

java.security.Principal

反映 WebSocket HTTP 握手時登入的使用者。

傳回值

預設情況下,@MessageMapping 方法的傳回值會透過匹配的 MessageConverter 序列化為酬載,並作為 Message 傳送到 brokerChannel,然後從那裡廣播給訂閱者。輸出訊息的目的地與輸入訊息的目的地相同,但以 /topic 為前綴。

您可以使用 @SendTo@SendToUser 註解來自訂輸出訊息的目的地。@SendTo 用於自訂目標目的地或指定多個目的地。@SendToUser 用於將輸出訊息僅導向到與輸入訊息相關聯的使用者。請參閱使用者目的地

您可以同時在同一個方法上使用 @SendTo@SendToUser,並且兩者都在類別層級受到支援,在這種情況下,它們充當類別中方法的預設值。但是,請記住,任何方法層級的 @SendTo@SendToUser 註解都會覆寫類別層級的任何此類註解。

訊息可以非同步處理,而 @MessageMapping 方法可以傳回 ListenableFutureCompletableFutureCompletionStage

請注意,@SendTo@SendToUser 僅僅是一種便利,相當於使用 SimpMessagingTemplate 來傳送訊息。如有必要,對於更進階的場景,@MessageMapping 方法可以退回到直接使用 SimpMessagingTemplate。這可以取代或可能除了傳回值之外完成。請參閱傳送訊息

@SubscribeMapping

@SubscribeMapping 類似於 @MessageMapping,但將對應範圍縮小到僅限訂閱訊息。它支援與 @MessageMapping 相同的方法引數。但是對於傳回值,預設情況下,訊息會直接傳送到客户端(透過 clientOutboundChannel,以回應訂閱),而不是傳送到 Broker(透過 brokerChannel,作為對匹配訂閱的廣播)。新增 @SendTo@SendToUser 會覆寫此行為,並改為傳送到 Broker。

這在什麼時候有用?假設 Broker 對應到 /topic/queue,而應用程式控制器對應到 /app。在此設定中,Broker 儲存所有針對 /topic/queue 的訂閱,這些訂閱旨在用於重複廣播,並且應用程式無需介入。客户端也可以訂閱某些 /app 目的地,而控制器可以傳回一個值以回應該訂閱,而無需 Broker 參與,也無需再次儲存或使用該訂閱(有效地說是一次性的請求-回覆交換)。這種情況的一個用例是在啟動時使用初始資料填充 UI。

這在什麼時候沒有用?除非您希望 Broker 和控制器都獨立處理訊息(包括訂閱),否則請勿嘗試將 Broker 和控制器對應到相同的目的地前綴。傳入訊息是平行處理的。無法保證 Broker 或控制器哪個先處理給定的訊息。如果目標是在儲存訂閱並準備好進行廣播時收到通知,則如果伺服器支援回條,客户端應要求回條(簡單 Broker 不支援)。例如,使用 Java STOMP 客户端,您可以執行以下操作來新增回條

@Autowired
private TaskScheduler messageBrokerTaskScheduler;

// During initialization..
stompClient.setTaskScheduler(this.messageBrokerTaskScheduler);

// When subscribing..
StompHeaders headers = new StompHeaders();
headers.setDestination("/topic/...");
headers.setReceipt("r1");
FrameHandler handler = ...;
stompSession.subscribe(headers, handler).addReceiptTask(receiptHeaders -> {
	// Subscription ready...
});

伺服器端選項是註冊 ExecutorChannelInterceptorbrokerChannel 上,並實作 afterMessageHandled 方法,該方法在訊息(包括訂閱)處理完畢後調用。

@MessageExceptionHandler

應用程式可以使用 @MessageExceptionHandler 方法來處理來自 @MessageMapping 方法的例外。您可以在註解本身中宣告例外,或者如果您想存取例外實例,可以透過方法引數來宣告。以下範例透過方法引數宣告例外

@Controller
public class MyController {

	// ...

	@MessageExceptionHandler
	public ApplicationError handleException(MyException exception) {
		// ...
		return appError;
	}
}

@MessageExceptionHandler 方法支援彈性的方法簽章,並支援與 @MessageMapping 方法相同的方法引數型別和傳回值。

通常,@MessageExceptionHandler 方法在其宣告的 @Controller 類別(或類別階層)中應用。如果您希望這些方法更全域地應用(跨控制器),則可以在標記為 @ControllerAdvice 的類別中宣告它們。這與 Spring MVC 中提供的類似支援相當。