0
# Annotation-Based Handling
1
2
Declarative message handling using annotations for mapping messages to handler methods, extracting payloads and headers, and defining response destinations.
3
4
## Capabilities
5
6
### Core Annotations
7
8
Primary annotations for mapping messages to handler methods and extracting message components.
9
10
```java { .api }
11
/**
12
* Annotation for mapping a Message onto a message-handling method by matching
13
* the declared patterns to a destination extracted from the message.
14
*/
15
@Target({ElementType.TYPE, ElementType.METHOD})
16
@Retention(RetentionPolicy.RUNTIME)
17
@Documented
18
public @interface MessageMapping {
19
/**
20
* Destination patterns to map to. Supports Ant-style path patterns with placeholders.
21
*/
22
String[] value() default {};
23
}
24
25
/**
26
* Annotation that binds a method parameter to the payload of a message.
27
*/
28
@Target(ElementType.PARAMETER)
29
@Retention(RetentionPolicy.RUNTIME)
30
@Documented
31
public @interface Payload {
32
/**
33
* A SpEL expression to be evaluated against the payload object as the root context.
34
*/
35
String value() default "";
36
37
/**
38
* Whether payload content is required.
39
*/
40
boolean required() default true;
41
}
42
43
/**
44
* Annotation which indicates that a method parameter should be bound to a message header.
45
*/
46
@Target(ElementType.PARAMETER)
47
@Retention(RetentionPolicy.RUNTIME)
48
@Documented
49
public @interface Header {
50
/**
51
* The name of the request header to bind to.
52
*/
53
String value() default "";
54
55
/**
56
* The name of the request header to bind to.
57
*/
58
String name() default "";
59
60
/**
61
* Whether the header is required.
62
*/
63
boolean required() default true;
64
65
/**
66
* The default value as a fallback.
67
*/
68
String defaultValue() default ValueConstants.DEFAULT_NONE;
69
}
70
71
/**
72
* Annotation which indicates that a method parameter should be bound to the headers of a message.
73
*/
74
@Target(ElementType.PARAMETER)
75
@Retention(RetentionPolicy.RUNTIME)
76
@Documented
77
public @interface Headers {
78
// No attributes - binds all headers as a Map
79
}
80
```
81
82
### Destination and Response Annotations
83
84
Annotations for handling destination variables and specifying response destinations.
85
86
```java { .api }
87
/**
88
* Annotation to indicate that a method parameter is bound to a URI template variable.
89
*/
90
@Target(ElementType.PARAMETER)
91
@Retention(RetentionPolicy.RUNTIME)
92
@Documented
93
public @interface DestinationVariable {
94
/**
95
* The name of the destination template variable to bind to.
96
*/
97
String value() default "";
98
99
/**
100
* Whether the destination variable is required.
101
*/
102
boolean required() default true;
103
}
104
105
/**
106
* Annotation for sending a message as a response to the current message.
107
*/
108
@Target({ElementType.TYPE, ElementType.METHOD})
109
@Retention(RetentionPolicy.RUNTIME)
110
@Documented
111
public @interface SendTo {
112
/**
113
* Destination where the return value should be sent.
114
*/
115
String[] value() default {};
116
}
117
118
/**
119
* Annotation for sending a message to the specified user-specific destination.
120
*/
121
@Target({ElementType.TYPE, ElementType.METHOD})
122
@Retention(RetentionPolicy.RUNTIME)
123
@Documented
124
public @interface SendToUser {
125
/**
126
* User destinations where the return value should be sent.
127
*/
128
String[] value() default {};
129
130
/**
131
* Whether to broadcast to all sessions of the user.
132
*/
133
boolean broadcast() default true;
134
}
135
```
136
137
### Exception Handling Annotations
138
139
Annotations for handling exceptions in message processing.
140
141
```java { .api }
142
/**
143
* Annotation for handling exceptions thrown from message-handling methods within a specific handler class.
144
*/
145
@Target(ElementType.METHOD)
146
@Retention(RetentionPolicy.RUNTIME)
147
@Documented
148
public @interface MessageExceptionHandler {
149
/**
150
* Exceptions handled by the annotated method.
151
*/
152
Class<? extends Throwable>[] value() default {};
153
}
154
```
155
156
### SIMP-Specific Annotations
157
158
Annotations specific to SIMP (Simple Messaging Protocol) for WebSocket messaging.
159
160
```java { .api }
161
/**
162
* Annotation for mapping subscription requests onto specific handler methods based on the destination.
163
*/
164
@Target(ElementType.METHOD)
165
@Retention(RetentionPolicy.RUNTIME)
166
@Documented
167
public @interface SubscribeMapping {
168
/**
169
* Destination patterns for subscription requests.
170
*/
171
String[] value() default {};
172
}
173
```
174
175
### RSocket-Specific Annotations
176
177
Annotations specific to RSocket protocol handling.
178
179
```java { .api }
180
/**
181
* Annotation for mapping RSocket connection requests onto handler methods.
182
*/
183
@Target(ElementType.METHOD)
184
@Retention(RetentionPolicy.RUNTIME)
185
@Documented
186
public @interface ConnectMapping {
187
/**
188
* Patterns for matching connection setup frame routes.
189
*/
190
String[] value() default {};
191
}
192
```
193
194
### Constants
195
196
Constants used in annotation default values.
197
198
```java { .api }
199
/**
200
* Common value constants shared between bind annotations.
201
*/
202
public final class ValueConstants {
203
204
/**
205
* Constant defining a value for "default none" - used as a marker for a default
206
* value that hasn't been provided and should be resolved in another way.
207
*/
208
public static final String DEFAULT_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n";
209
210
private ValueConstants() {
211
// Private constructor
212
}
213
}
214
```
215
216
**Usage Examples:**
217
218
```java
219
import org.springframework.messaging.handler.annotation.*;
220
import org.springframework.messaging.simp.annotation.*;
221
import org.springframework.stereotype.Controller;
222
223
@Controller
224
public class ChatController {
225
226
// Basic message mapping
227
@MessageMapping("/chat.sendMessage")
228
@SendTo("/topic/public")
229
public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
230
return chatMessage;
231
}
232
233
// Extract specific headers
234
@MessageMapping("/chat.addUser")
235
@SendTo("/topic/public")
236
public ChatMessage addUser(@Payload ChatMessage chatMessage,
237
@Header("simpSessionId") String sessionId,
238
@Header(value = "priority", defaultValue = "normal") String priority) {
239
chatMessage.setSender(sessionId);
240
return chatMessage;
241
}
242
243
// Use destination variables (path parameters)
244
@MessageMapping("/chat.room.{roomId}")
245
@SendTo("/topic/room.{roomId}")
246
public ChatMessage sendToRoom(@DestinationVariable String roomId,
247
@Payload ChatMessage message) {
248
message.setRoom(roomId);
249
return message;
250
}
251
252
// Access all headers
253
@MessageMapping("/chat.debug")
254
public void debugMessage(@Payload String message,
255
@Headers Map<String, Object> headers) {
256
System.out.println("Message: " + message);
257
System.out.println("Headers: " + headers);
258
}
259
260
// User-specific messaging
261
@MessageMapping("/chat.private")
262
@SendToUser("/queue/reply")
263
public String handlePrivateMessage(@Payload String message,
264
Principal principal) {
265
return "Hello " + principal.getName() + ", you sent: " + message;
266
}
267
268
// Subscription mapping for WebSocket
269
@SubscribeMapping("/topic/activity")
270
public ActivityMessage getActivity() {
271
return new ActivityMessage("Current activity status");
272
}
273
274
// Exception handling
275
@MessageExceptionHandler(IllegalArgumentException.class)
276
@SendToUser("/queue/errors")
277
public String handleException(IllegalArgumentException ex) {
278
return "Error: " + ex.getMessage();
279
}
280
281
// Multiple exception types
282
@MessageExceptionHandler({IllegalArgumentException.class, IllegalStateException.class})
283
public void handleMultipleExceptions(Exception ex) {
284
// Handle multiple exception types
285
}
286
287
// RSocket connection mapping
288
@ConnectMapping("setup")
289
public void handleConnection(@Header Map<String, Object> metadata) {
290
// Handle RSocket connection setup
291
}
292
}
293
294
// Example with class-level mapping
295
@Controller
296
@MessageMapping("/app")
297
public class AppController {
298
299
// Inherits "/app" prefix, so actual mapping is "/app/hello"
300
@MessageMapping("/hello")
301
@SendTo("/topic/greetings")
302
public Greeting greeting(@Payload HelloMessage message) {
303
return new Greeting("Hello, " + message.getName() + "!");
304
}
305
}
306
```