0
# Event System
1
2
Reactive event publishing and listening system for decoupled communication between application components. Supports strongly-typed events, automatic event listener registration, and comprehensive application lifecycle events.
3
4
## Capabilities
5
6
### @EventListener Annotation
7
8
Declarative event listener registration for automatic event handling.
9
10
```java { .api }
11
/**
12
* Annotation for methods that should listen for application events
13
* Automatically registers the method as an ApplicationEventListener
14
*/
15
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
16
@Retention(RetentionPolicy.RUNTIME)
17
@Adapter(ApplicationEventListener.class)
18
public @interface EventListener {
19
// Marker annotation - no attributes needed
20
// Event type is determined from method parameter type
21
}
22
```
23
24
**Usage Examples:**
25
26
```java
27
import io.micronaut.runtime.event.annotation.EventListener;
28
import io.micronaut.runtime.server.event.ServerStartupEvent;
29
import io.micronaut.runtime.server.event.ServerShutdownEvent;
30
import io.micronaut.context.event.StartupEvent;
31
import io.micronaut.context.event.ShutdownEvent;
32
import jakarta.inject.Singleton;
33
34
@Singleton
35
public class ApplicationEventListener {
36
37
@EventListener
38
public void onStartup(ServerStartupEvent event) {
39
EmbeddedServer server = event.getSource();
40
System.out.println("Server started on: " + server.getURL());
41
System.out.println("Application ready to handle requests");
42
}
43
44
@EventListener
45
public void onShutdown(ServerShutdownEvent event) {
46
System.out.println("Server shutting down");
47
// Cleanup resources, close connections, etc.
48
}
49
50
@EventListener
51
public void onApplicationStartup(StartupEvent event) {
52
ApplicationContext context = event.getSource();
53
System.out.println("Application context started");
54
}
55
56
@EventListener
57
public void onApplicationShutdown(ShutdownEvent event) {
58
System.out.println("Application context shutting down");
59
}
60
61
// Custom event listener
62
@EventListener
63
public void onCustomEvent(CustomBusinessEvent event) {
64
System.out.println("Business event: " + event.getData());
65
}
66
67
// Async event handling
68
@EventListener
69
@Async
70
public CompletableFuture<Void> onAsyncEvent(AsyncProcessingEvent event) {
71
return CompletableFuture.runAsync(() -> {
72
// Long-running event processing
73
processEventData(event.getData());
74
});
75
}
76
}
77
```
78
79
### Application Lifecycle Events
80
81
Built-in events for tracking application and server lifecycle.
82
83
```java { .api }
84
/**
85
* Base class for embedded application events
86
* Provides common functionality for application lifecycle events
87
*/
88
public abstract class AbstractEmbeddedApplicationEvent extends ApplicationEvent {
89
/**
90
* Create application event with embedded application source
91
* @param source The embedded application that generated the event
92
*/
93
public AbstractEmbeddedApplicationEvent(EmbeddedApplication<?> source);
94
95
/**
96
* Get the embedded application that generated this event
97
* @return EmbeddedApplication instance
98
*/
99
public EmbeddedApplication<?> getSource();
100
}
101
102
/**
103
* Event fired when EmbeddedApplication starts up
104
* Indicates the application context is ready and services are available
105
*/
106
public class ApplicationStartupEvent extends AbstractEmbeddedApplicationEvent {
107
/**
108
* Create startup event
109
* @param source The embedded application that started
110
*/
111
public ApplicationStartupEvent(EmbeddedApplication<?> source);
112
}
113
114
/**
115
* Event fired when EmbeddedApplication shuts down
116
* Last chance to perform cleanup before application termination
117
*/
118
public class ApplicationShutdownEvent extends AbstractEmbeddedApplicationEvent {
119
/**
120
* Create shutdown event
121
* @param source The embedded application that is shutting down
122
*/
123
public ApplicationShutdownEvent(EmbeddedApplication<?> source);
124
}
125
```
126
127
### Server Lifecycle Events
128
129
Events specific to embedded server lifecycle for web applications.
130
131
```java { .api }
132
/**
133
* Event fired when EmbeddedServer completes startup
134
* Server is ready to accept HTTP requests
135
*/
136
public class ServerStartupEvent extends ApplicationStartupEvent {
137
/**
138
* Create server startup event
139
* @param embeddedServer The server that started
140
*/
141
public ServerStartupEvent(EmbeddedServer embeddedServer);
142
143
/**
144
* Get the embedded server that started
145
* @return EmbeddedServer instance
146
*/
147
public EmbeddedServer getSource();
148
}
149
150
/**
151
* Event fired when EmbeddedServer shuts down
152
* Server will no longer accept new requests
153
*/
154
public class ServerShutdownEvent extends ApplicationEvent {
155
/**
156
* Create server shutdown event
157
* @param embeddedServer The server that is shutting down
158
*/
159
public ServerShutdownEvent(EmbeddedServer embeddedServer);
160
161
/**
162
* Get the embedded server that is shutting down
163
* @return EmbeddedServer instance
164
*/
165
public EmbeddedServer getSource();
166
}
167
```
168
169
**Usage Examples:**
170
171
```java
172
import io.micronaut.runtime.server.event.ServerStartupEvent;
173
import io.micronaut.runtime.server.event.ServerShutdownEvent;
174
import io.micronaut.runtime.event.annotation.EventListener;
175
176
@Singleton
177
public class ServerLifecycleListener {
178
179
@EventListener
180
public void onServerStartup(ServerStartupEvent event) {
181
EmbeddedServer server = event.getSource();
182
183
// Log server startup details
184
System.out.println("π Server started successfully!");
185
System.out.println(" URL: " + server.getURL());
186
System.out.println(" Port: " + server.getPort());
187
System.out.println(" Host: " + server.getHost());
188
189
// Initialize external services
190
initializeExternalConnections();
191
192
// Register with service discovery
193
registerWithServiceRegistry(server);
194
}
195
196
@EventListener
197
public void onServerShutdown(ServerShutdownEvent event) {
198
System.out.println("π Server shutting down...");
199
200
// Deregister from service discovery
201
deregisterFromServiceRegistry();
202
203
// Close external connections
204
closeExternalConnections();
205
206
System.out.println("β Graceful shutdown completed");
207
}
208
}
209
```
210
211
### Event Publishing
212
213
Programmatic event publishing for custom business events.
214
215
```java { .api }
216
/**
217
* ApplicationEventPublisher interface for publishing events
218
* Automatically available as an injectable bean
219
*/
220
public interface ApplicationEventPublisher<T> {
221
/**
222
* Publish an event to all registered listeners
223
* Executes synchronously, blocking until all listeners complete
224
* @param event The event to publish
225
*/
226
void publishEvent(@NonNull T event);
227
228
/**
229
* Publish an event asynchronously
230
* @param event The event to publish
231
* @return Future representing the async operation
232
*/
233
@NonNull Future<Void> publishEventAsync(@NonNull T event);
234
235
/**
236
* Check whether this publisher is empty (has no listeners)
237
* @return true if there are no subscribers
238
*/
239
boolean isEmpty();
240
241
/**
242
* Returns a no-op instance of ApplicationEventPublisher
243
* @param <K> The event type
244
* @return no-op ApplicationEventPublisher instance
245
*/
246
static <K> ApplicationEventPublisher<K> noOp();
247
}
248
```
249
250
**Usage Examples:**
251
252
```java
253
import io.micronaut.context.event.ApplicationEventPublisher;
254
import jakarta.inject.Inject;
255
import jakarta.inject.Singleton;
256
257
// Custom business event
258
public class OrderProcessedEvent {
259
private final String orderId;
260
private final BigDecimal amount;
261
private final Instant processedAt;
262
263
public OrderProcessedEvent(String orderId, BigDecimal amount) {
264
this.orderId = orderId;
265
this.amount = amount;
266
this.processedAt = Instant.now();
267
}
268
269
// Getters...
270
public String getOrderId() { return orderId; }
271
public BigDecimal getAmount() { return amount; }
272
public Instant getProcessedAt() { return processedAt; }
273
}
274
275
@Singleton
276
public class OrderService {
277
278
@Inject
279
private ApplicationEventPublisher<OrderProcessedEvent> eventPublisher;
280
281
public void processOrder(Order order) {
282
// Process the order
283
doOrderProcessing(order);
284
285
// Publish event synchronously
286
OrderProcessedEvent event = new OrderProcessedEvent(
287
order.getId(),
288
order.getTotal()
289
);
290
eventPublisher.publishEvent(event);
291
}
292
293
public CompletableFuture<Void> processOrderAsync(Order order) {
294
return CompletableFuture.runAsync(() -> {
295
doOrderProcessing(order);
296
}).thenCompose(v -> {
297
// Publish event asynchronously
298
OrderProcessedEvent event = new OrderProcessedEvent(
299
order.getId(),
300
order.getTotal()
301
);
302
return eventPublisher.publishEventAsync(event);
303
});
304
}
305
}
306
307
// Event listeners for the custom event
308
@Singleton
309
public class OrderEventHandlers {
310
311
@EventListener
312
public void onOrderProcessed(OrderProcessedEvent event) {
313
System.out.println("Order processed: " + event.getOrderId());
314
315
// Send confirmation email
316
sendConfirmationEmail(event.getOrderId());
317
}
318
319
@EventListener
320
@Async
321
public CompletableFuture<Void> updateAnalytics(OrderProcessedEvent event) {
322
return CompletableFuture.runAsync(() -> {
323
// Update business analytics asynchronously
324
analyticsService.recordOrderProcessed(event);
325
});
326
}
327
328
@EventListener
329
public void auditOrderProcessing(OrderProcessedEvent event) {
330
auditService.log("ORDER_PROCESSED", event.getOrderId(), event.getAmount());
331
}
332
}
333
```
334
335
### Conditional Event Listeners
336
337
Event listeners with conditional execution based on application configuration.
338
339
```java { .api }
340
/**
341
* Conditional event listener execution
342
* Use @Requires annotation to control when listeners are active
343
*/
344
import io.micronaut.context.annotation.Requires;
345
346
@Singleton
347
@Requires(property = "app.monitoring.enabled", value = "true")
348
public class MonitoringEventListener {
349
350
@EventListener
351
public void onServerStartup(ServerStartupEvent event) {
352
// Only active when monitoring is enabled
353
startupMonitor.recordStartup(event);
354
}
355
}
356
357
@Singleton
358
@Requires(env = "production")
359
public class ProductionEventListener {
360
361
@EventListener
362
public void onApplicationStartup(ApplicationStartupEvent event) {
363
// Only active in production environment
364
productionMetrics.recordStartup();
365
}
366
}
367
```
368
369
### Error Handling in Event Listeners
370
371
Best practices for handling exceptions in event listeners.
372
373
```java { .api }
374
@Singleton
375
public class RobustEventListener {
376
377
private static final Logger logger = LoggerFactory.getLogger(RobustEventListener.class);
378
379
@EventListener
380
public void onServerStartup(ServerStartupEvent event) {
381
try {
382
// Event processing logic
383
performStartupOperations(event);
384
} catch (Exception e) {
385
// Log error but don't propagate to prevent disrupting other listeners
386
logger.error("Error processing server startup event", e);
387
388
// Optional: publish error event for monitoring
389
errorEventPublisher.publishEvent(new EventProcessingError(e));
390
}
391
}
392
393
@EventListener
394
@Retryable(attempts = "3", delay = "1s")
395
public void onRetryableEvent(CustomEvent event) {
396
// This listener will be retried up to 3 times with 1-second delay
397
riskyOperation(event);
398
}
399
}
400
```
401
402
## Custom Event Types
403
404
### Creating Custom Events
405
406
Guidelines for creating custom event types.
407
408
```java { .api }
409
/**
410
* Base pattern for custom business events
411
* Extend ApplicationEvent or create POJO with relevant data
412
*/
413
public class CustomBusinessEvent extends ApplicationEvent {
414
private final String businessData;
415
private final Instant timestamp;
416
417
public CustomBusinessEvent(Object source, String businessData) {
418
super(source);
419
this.businessData = businessData;
420
this.timestamp = Instant.now();
421
}
422
423
public String getBusinessData() { return businessData; }
424
public Instant getTimestamp() { return timestamp; }
425
}
426
427
// Alternative: Simple POJO event (recommended for most cases)
428
public class UserRegisteredEvent {
429
private final String userId;
430
private final String email;
431
private final Instant registeredAt;
432
433
public UserRegisteredEvent(String userId, String email) {
434
this.userId = userId;
435
this.email = email;
436
this.registeredAt = Instant.now();
437
}
438
439
// Getters...
440
}
441
```
442
443
**Complete Custom Event Example:**
444
445
```java
446
// Event definition
447
public class PaymentProcessedEvent {
448
private final String paymentId;
449
private final String customerId;
450
private final BigDecimal amount;
451
private final String currency;
452
private final PaymentStatus status;
453
private final Instant processedAt;
454
455
// Constructor and getters...
456
}
457
458
// Event publisher service
459
@Singleton
460
public class PaymentService {
461
462
@Inject
463
private ApplicationEventPublisher<PaymentProcessedEvent> eventPublisher;
464
465
public void processPayment(PaymentRequest request) {
466
// Process payment logic
467
PaymentResult result = processPaymentInternal(request);
468
469
// Publish event
470
PaymentProcessedEvent event = new PaymentProcessedEvent(
471
result.getPaymentId(),
472
request.getCustomerId(),
473
request.getAmount(),
474
request.getCurrency(),
475
result.getStatus()
476
);
477
478
eventPublisher.publishEvent(event);
479
}
480
}
481
482
// Multiple event listeners
483
@Singleton
484
public class PaymentEventHandlers {
485
486
@EventListener
487
public void onPaymentProcessed(PaymentProcessedEvent event) {
488
if (event.getStatus() == PaymentStatus.SUCCESS) {
489
// Send success notification
490
notificationService.sendPaymentConfirmation(event);
491
} else {
492
// Handle payment failure
493
alertService.notifyPaymentFailure(event);
494
}
495
}
496
497
@EventListener
498
@Async
499
public CompletableFuture<Void> updateCustomerBalance(PaymentProcessedEvent event) {
500
return CompletableFuture.runAsync(() -> {
501
customerService.updateBalance(event.getCustomerId(), event.getAmount());
502
});
503
}
504
505
@EventListener
506
public void auditPayment(PaymentProcessedEvent event) {
507
auditService.recordPayment(event);
508
}
509
}
510
```