註解控制器
應用程式可以使用帶有註解的 @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
,作為對訂閱的回應),而不是傳送給 broker(透過 brokerChannel
,作為對匹配訂閱的廣播)。新增 @SendTo
或 @SendToUser
會覆蓋此行為,轉而傳送給 broker。
這什麼時候有用?假設 broker 對映到 /topic
和 /queue
,而應用控制器對映到 /app
。在這種設定下,broker 會儲存所有旨在重複廣播到 /topic
和 /queue
的訂閱,應用程式無需介入。客戶端也可以訂閱某個 /app
目的地,並且控制器可以對該訂閱返回一個值作為響應,而無需涉及 broker,也無需再次儲存或使用該訂閱(實質上是一次性的請求-回覆交換)。一個用例是在啟動時用初始資料填充 UI。
這什麼時候沒用?除非您出於某種原因希望 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...
});
伺服器端的一種選擇是在 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 中可用的類似支援相當。