帶註解的控制器
應用程式可以使用帶註解的 @Controller 類來處理來自客戶端的訊息。此類可以宣告 @MessageMapping、@SubscribeMapping 和 @ExceptionHandler 方法,如下所述:
@MessageMapping
您可以使用 @MessageMapping 註解方法,根據訊息目的地路由訊息。它支援方法級別和型別級別。在型別級別上,@MessageMapping 用於表示控制器中所有方法共享的對映。
預設情況下,對映值是 Ant 風格的路徑模式(例如 /thing*、/thing/**),包括支援模板變數(例如 /thing/{id})。這些值可以透過 @DestinationVariable 方法引數引用。應用程式還可以切換到以點分隔的目的地約定進行對映,如作為分隔符的點中所述。
支援的方法引數
下表描述了方法引數:
| 方法引數 | 描述 |
|---|---|
|
用於訪問完整訊息。 |
|
用於訪問 |
|
透過型別化訪問器方法訪問頭部。 |
|
用於訪問訊息負載,透過配置的 此註解不是必需的,因為它在預設情況下,如果沒有其他引數匹配,則假定存在。 您可以使用 |
|
用於訪問特定頭部值 — 必要時,透過 |
|
用於訪問訊息中的所有頭部。此引數必須可賦值給 |
|
用於訪問從訊息目的地提取的模板變數。必要時,將值轉換為宣告的方法引數型別。 |
|
反映 WebSocket HTTP 握手時登入的使用者。 |
返回值
預設情況下,@MessageMapping 方法的返回值透過匹配的 MessageConverter 序列化為負載,並作為 Message 傳送到 brokerChannel,然後從那裡廣播給訂閱者。出站訊息的目的地與入站訊息的目的地相同,但字首為 /topic。
您可以使用 @SendTo 和 @SendToUser 註解自定義輸出訊息的目的地。@SendTo 用於自定義目標目的地或指定多個目的地。@SendToUser 用於將輸出訊息僅傳送給與輸入訊息關聯的使用者。請參閱使用者目的地。
您可以在同一方法上同時使用 @SendTo 和 @SendToUser,並且它們都支援在類級別使用,在這種情況下,它們充當類中方法的預設值。但是,請記住,任何方法級別的 @SendTo 或 @SendToUser 註解都會覆蓋類級別的任何此類註解。
訊息可以非同步處理,@MessageMapping 方法可以返回 ListenableFuture、CompletableFuture 或 CompletionStage。
請注意,@SendTo 和 @SendToUser 僅僅是方便用法,相當於使用 SimpMessagingTemplate 傳送訊息。如果需要,對於更高階的場景,@MessageMapping 方法可以直接使用 SimpMessagingTemplate。這可以代替或可能額外返回一個值。請參閱傳送訊息。
@SubscribeMapping
@SubscribeMapping 類似於 @MessageMapping,但將對映範圍縮小到僅訂閱訊息。它支援與 @MessageMapping 相同的方法引數。但是對於返回值,預設情況下,訊息直接傳送到客戶端(透過 clientOutboundChannel,作為對訂閱的響應),而不是傳送到代理(透過 brokerChannel,作為對匹配訂閱的廣播)。新增 @SendTo 或 @SendToUser 會覆蓋此行為,並改為傳送到代理。
這何時有用?假設代理對映到 /topic 和 /queue,而應用程式控制器對映到 /app。在這種設定下,代理儲存所有旨在重複廣播的 /topic 和 /queue 訂閱,應用程式無需參與。客戶端也可以訂閱某個 /app 目的地,控制器可以響應該訂閱返回一個值,而無需涉及代理,也無需再次儲存或使用該訂閱(實際上是一次性請求-回覆交換)。一個用例是在啟動時用初始資料填充 UI。
這何時無用?除非您希望代理和控制器都以某種原因獨立處理訊息(包括訂閱),否則不要嘗試將它們對映到相同的字首。入站訊息是並行處理的。無法保證代理或控制器首先處理給定訊息。如果目標是在訂閱儲存並準備好進行廣播時收到通知,如果伺服器支援(簡單代理不支援),客戶端應該請求收據。例如,使用 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...
});
伺服器端選項是在 brokerChannel 上註冊一個 ExecutorChannelInterceptor 並實現 afterMessageHandled 方法,該方法在訊息(包括訂閱)處理後呼叫。
@MessageExceptionHandler
應用程式可以使用 @MessageExceptionHandler 方法來處理來自 @MessageMapping 方法的異常。您可以在註解本身中宣告異常,或者如果您想訪問異常例項,可以透過方法引數宣告。以下示例透過方法引數宣告異常:
@Controller
public class MyController {
// ...
@MessageExceptionHandler
public ApplicationError handleException(MyException exception) {
// ...
return appError;
}
}
@MessageExceptionHandler 方法支援靈活的方法簽名,並支援與 @MessageMapping 方法相同的方法引數型別和返回值。
通常,@MessageExceptionHandler 方法適用於它們宣告的 @Controller 類(或類層次結構)中。如果您希望這些方法更全域性地應用(跨控制器),您可以在用 @ControllerAdvice 標記的類中宣告它們。這與 Spring MVC 中可用的類似支援相當。