0
# Event System
1
2
Micronaut's event system provides a powerful mechanism for decoupled communication between components through application lifecycle events and custom business events. It supports both synchronous and asynchronous event processing.
3
4
## Core Interfaces
5
6
### ApplicationEventPublisher
7
8
Interface for publishing application events to registered listeners.
9
10
```java { .api }
11
public interface ApplicationEventPublisher<T> {
12
void publishEvent(T event);
13
Future<Void> publishEventAsync(T event);
14
}
15
```
16
17
### ApplicationEventListener
18
19
Interface for listening to application events.
20
21
```java { .api }
22
public interface ApplicationEventListener<E> {
23
void onApplicationEvent(E event);
24
default boolean supports(E event) { return true; }
25
}
26
```
27
28
### EventListener
29
30
Generic event listener interface for method-level event handling.
31
32
```java { .api }
33
@Target({METHOD})
34
@Retention(RUNTIME)
35
public @interface EventListener {
36
boolean async() default false;
37
}
38
```
39
40
## Built-in Application Events
41
42
### Startup Events
43
44
#### StartupEvent
45
46
Fired when the application context starts up.
47
48
```java { .api }
49
public class StartupEvent extends ApplicationEvent {
50
public StartupEvent(ApplicationContext source);
51
public ApplicationContext getSource();
52
}
53
```
54
55
#### ApplicationStartupEvent
56
57
More specific startup event with additional context.
58
59
```java { .api }
60
public class ApplicationStartupEvent extends ApplicationEvent {
61
public ApplicationStartupEvent(Application source);
62
public Application getSource();
63
}
64
```
65
66
### Shutdown Events
67
68
#### ShutdownEvent
69
70
Fired when the application context shuts down.
71
72
```java { .api }
73
public class ShutdownEvent extends ApplicationEvent {
74
public ShutdownEvent(ApplicationContext source);
75
public ApplicationContext getSource();
76
}
77
```
78
79
### Bean Lifecycle Events
80
81
#### BeanCreatedEvent
82
83
Fired when a bean is created.
84
85
```java { .api }
86
public class BeanCreatedEvent<T> extends ApplicationEvent {
87
public BeanCreatedEvent(BeanContext beanContext, BeanDefinition<T> beanDefinition, T bean);
88
public BeanDefinition<T> getBeanDefinition();
89
public T getBean();
90
}
91
```
92
93
#### BeanInitializedEvent
94
95
Fired when a bean is fully initialized.
96
97
```java { .api }
98
public class BeanInitializedEvent<T> extends ApplicationEvent {
99
public BeanInitializedEvent(BeanContext beanContext, BeanDefinition<T> beanDefinition, T bean);
100
public BeanDefinition<T> getBeanDefinition();
101
public T getBean();
102
}
103
```
104
105
#### BeanDestroyedEvent
106
107
Fired when a bean is destroyed.
108
109
```java { .api }
110
public class BeanDestroyedEvent<T> extends ApplicationEvent {
111
public BeanDestroyedEvent(BeanContext beanContext, BeanDefinition<T> beanDefinition, T bean);
112
public BeanDefinition<T> getBeanDefinition();
113
public T getBean();
114
}
115
```
116
117
## Event Publishing
118
119
### Publishing Events
120
121
```java
122
import io.micronaut.context.event.ApplicationEventPublisher;
123
import jakarta.inject.Singleton;
124
import jakarta.inject.Inject;
125
126
@Singleton
127
public class OrderService {
128
129
private final ApplicationEventPublisher<OrderEvent> eventPublisher;
130
131
@Inject
132
public OrderService(ApplicationEventPublisher<OrderEvent> eventPublisher) {
133
this.eventPublisher = eventPublisher;
134
}
135
136
public void createOrder(Order order) {
137
// Process order
138
order.setStatus(OrderStatus.CREATED);
139
140
// Publish event
141
OrderCreatedEvent event = new OrderCreatedEvent(order);
142
eventPublisher.publishEvent(event);
143
}
144
145
public void cancelOrder(String orderId) {
146
// Cancel order logic
147
Order order = findOrder(orderId);
148
order.setStatus(OrderStatus.CANCELLED);
149
150
// Publish cancellation event
151
OrderCancelledEvent event = new OrderCancelledEvent(order);
152
eventPublisher.publishEvent(event);
153
}
154
}
155
```
156
157
### Asynchronous Event Publishing
158
159
```java
160
import io.micronaut.context.event.ApplicationEventPublisher;
161
import jakarta.inject.Singleton;
162
import java.util.concurrent.Future;
163
164
@Singleton
165
public class NotificationService {
166
167
private final ApplicationEventPublisher<NotificationEvent> eventPublisher;
168
169
@Inject
170
public NotificationService(ApplicationEventPublisher<NotificationEvent> eventPublisher) {
171
this.eventPublisher = eventPublisher;
172
}
173
174
public void sendNotification(String message, String recipient) {
175
NotificationEvent event = new NotificationEvent(message, recipient);
176
177
// Publish asynchronously - doesn't block
178
Future<Void> future = eventPublisher.publishEventAsync(event);
179
180
// Optionally handle completion
181
future.thenRun(() -> {
182
System.out.println("Notification event published successfully");
183
}).exceptionally(throwable -> {
184
System.err.println("Failed to publish notification event: " + throwable.getMessage());
185
return null;
186
});
187
}
188
}
189
```
190
191
## Event Listening
192
193
### Interface-based Listeners
194
195
```java
196
import io.micronaut.context.event.ApplicationEventListener;
197
import jakarta.inject.Singleton;
198
199
@Singleton
200
public class OrderEventListener implements ApplicationEventListener<OrderCreatedEvent> {
201
202
@Override
203
public void onApplicationEvent(OrderCreatedEvent event) {
204
Order order = event.getOrder();
205
System.out.println("Order created: " + order.getId());
206
207
// Send confirmation email
208
sendConfirmationEmail(order);
209
210
// Update inventory
211
updateInventory(order);
212
}
213
214
@Override
215
public boolean supports(OrderCreatedEvent event) {
216
// Only handle events for orders over $100
217
return event.getOrder().getTotal().compareTo(new BigDecimal("100")) > 0;
218
}
219
220
private void sendConfirmationEmail(Order order) {
221
// Email sending logic
222
}
223
224
private void updateInventory(Order order) {
225
// Inventory update logic
226
}
227
}
228
```
229
230
### Method-based Listeners
231
232
```java
233
import io.micronaut.context.event.EventListener;
234
import jakarta.inject.Singleton;
235
236
@Singleton
237
public class ApplicationEventHandler {
238
239
@EventListener
240
public void onStartup(StartupEvent event) {
241
System.out.println("Application started: " + event.getSource());
242
// Initialize caches, warm up connections, etc.
243
}
244
245
@EventListener
246
public void onShutdown(ShutdownEvent event) {
247
System.out.println("Application shutting down: " + event.getSource());
248
// Cleanup resources, save state, etc.
249
}
250
251
@EventListener(async = true) // Asynchronous processing
252
public void onOrderCreated(OrderCreatedEvent event) {
253
// This runs in a separate thread
254
generateOrderReport(event.getOrder());
255
}
256
257
@EventListener
258
public void onBeanCreated(BeanCreatedEvent<?> event) {
259
if (event.getBean() instanceof DatabaseService) {
260
System.out.println("Database service bean created: " + event.getBeanDefinition().getBeanType());
261
}
262
}
263
}
264
```
265
266
### Generic Event Listeners
267
268
```java
269
import io.micronaut.context.event.ApplicationEventListener;
270
import jakarta.inject.Singleton;
271
272
@Singleton
273
public class GenericEventLogger implements ApplicationEventListener<ApplicationEvent> {
274
275
@Override
276
public void onApplicationEvent(ApplicationEvent event) {
277
System.out.println("Event received: " + event.getClass().getSimpleName() +
278
" at " + Instant.now());
279
}
280
281
@Override
282
public boolean supports(ApplicationEvent event) {
283
// Log all events except bean lifecycle events (too noisy)
284
return !(event instanceof BeanCreatedEvent ||
285
event instanceof BeanInitializedEvent ||
286
event instanceof BeanDestroyedEvent);
287
}
288
}
289
```
290
291
## Custom Events
292
293
### Creating Custom Events
294
295
```java
296
import io.micronaut.context.ApplicationEvent;
297
298
// Base order event
299
public abstract class OrderEvent extends ApplicationEvent {
300
private final Order order;
301
302
public OrderEvent(Order order) {
303
super(order);
304
this.order = order;
305
}
306
307
public Order getOrder() {
308
return order;
309
}
310
}
311
312
// Specific order events
313
public class OrderCreatedEvent extends OrderEvent {
314
public OrderCreatedEvent(Order order) {
315
super(order);
316
}
317
}
318
319
public class OrderCancelledEvent extends OrderEvent {
320
private final String reason;
321
322
public OrderCancelledEvent(Order order, String reason) {
323
super(order);
324
this.reason = reason;
325
}
326
327
public String getReason() {
328
return reason;
329
}
330
}
331
332
public class OrderShippedEvent extends OrderEvent {
333
private final String trackingNumber;
334
private final String carrier;
335
336
public OrderShippedEvent(Order order, String trackingNumber, String carrier) {
337
super(order);
338
this.trackingNumber = trackingNumber;
339
this.carrier = carrier;
340
}
341
342
public String getTrackingNumber() {
343
return trackingNumber;
344
}
345
346
public String getCarrier() {
347
return carrier;
348
}
349
}
350
```
351
352
### Business Event Examples
353
354
```java
355
// User events
356
public class UserRegisteredEvent extends ApplicationEvent {
357
private final User user;
358
359
public UserRegisteredEvent(User user) {
360
super(user);
361
this.user = user;
362
}
363
364
public User getUser() {
365
return user;
366
}
367
}
368
369
public class UserLoginEvent extends ApplicationEvent {
370
private final String username;
371
private final String ipAddress;
372
private final Instant loginTime;
373
374
public UserLoginEvent(String username, String ipAddress) {
375
super(username);
376
this.username = username;
377
this.ipAddress = ipAddress;
378
this.loginTime = Instant.now();
379
}
380
381
// Getters...
382
}
383
384
// Payment events
385
public class PaymentProcessedEvent extends ApplicationEvent {
386
private final Payment payment;
387
private final PaymentResult result;
388
389
public PaymentProcessedEvent(Payment payment, PaymentResult result) {
390
super(payment);
391
this.payment = payment;
392
this.result = result;
393
}
394
395
// Getters...
396
}
397
```
398
399
## Event Processing Patterns
400
401
### Event Aggregation
402
403
```java
404
import io.micronaut.context.event.EventListener;
405
import jakarta.inject.Singleton;
406
import java.util.concurrent.ConcurrentHashMap;
407
import java.util.concurrent.atomic.AtomicInteger;
408
409
@Singleton
410
public class EventAggregator {
411
412
private final Map<String, AtomicInteger> eventCounts = new ConcurrentHashMap<>();
413
414
@EventListener
415
public void onOrderEvent(OrderEvent event) {
416
String eventType = event.getClass().getSimpleName();
417
eventCounts.computeIfAbsent(eventType, k -> new AtomicInteger(0)).incrementAndGet();
418
}
419
420
@EventListener
421
public void onUserEvent(UserEvent event) {
422
String eventType = event.getClass().getSimpleName();
423
eventCounts.computeIfAbsent(eventType, k -> new AtomicInteger(0)).incrementAndGet();
424
}
425
426
public Map<String, Integer> getEventStats() {
427
return eventCounts.entrySet().stream()
428
.collect(Collectors.toMap(
429
Map.Entry::getKey,
430
e -> e.getValue().get()
431
));
432
}
433
}
434
```
435
436
### Event Chain Processing
437
438
```java
439
import io.micronaut.context.event.EventListener;
440
import io.micronaut.context.event.ApplicationEventPublisher;
441
import jakarta.inject.Singleton;
442
443
@Singleton
444
public class OrderProcessingChain {
445
446
private final ApplicationEventPublisher<OrderEvent> eventPublisher;
447
448
@Inject
449
public OrderProcessingChain(ApplicationEventPublisher<OrderEvent> eventPublisher) {
450
this.eventPublisher = eventPublisher;
451
}
452
453
@EventListener
454
public void onOrderCreated(OrderCreatedEvent event) {
455
Order order = event.getOrder();
456
457
// Validate order
458
if (validateOrder(order)) {
459
eventPublisher.publishEvent(new OrderValidatedEvent(order));
460
} else {
461
eventPublisher.publishEvent(new OrderRejectedEvent(order, "Validation failed"));
462
}
463
}
464
465
@EventListener
466
public void onOrderValidated(OrderValidatedEvent event) {
467
Order order = event.getOrder();
468
469
// Process payment
470
PaymentResult result = processPayment(order);
471
if (result.isSuccessful()) {
472
eventPublisher.publishEvent(new OrderPaymentCompletedEvent(order));
473
} else {
474
eventPublisher.publishEvent(new OrderPaymentFailedEvent(order, result.getError()));
475
}
476
}
477
478
@EventListener
479
public void onPaymentCompleted(OrderPaymentCompletedEvent event) {
480
Order order = event.getOrder();
481
482
// Ship order
483
String trackingNumber = shipOrder(order);
484
eventPublisher.publishEvent(new OrderShippedEvent(order, trackingNumber, "UPS"));
485
}
486
}
487
```
488
489
### Conditional Event Processing
490
491
```java
492
import io.micronaut.context.event.EventListener;
493
import io.micronaut.context.annotation.Requires;
494
import jakarta.inject.Singleton;
495
496
@Singleton
497
@Requires(property = "analytics.enabled", value = "true")
498
public class AnalyticsEventProcessor {
499
500
@EventListener
501
public void onUserAction(UserActionEvent event) {
502
// Only process if analytics is enabled
503
recordUserAction(event);
504
}
505
506
@EventListener
507
public void onOrderCompleted(OrderCompletedEvent event) {
508
recordSale(event.getOrder());
509
updateRevenueMetrics(event.getOrder());
510
}
511
}
512
513
@Singleton
514
@Requires(env = "prod")
515
public class ProductionEventMonitor {
516
517
@EventListener
518
public void onError(ErrorEvent event) {
519
// Only monitor errors in production
520
alertOpsTeam(event);
521
}
522
}
523
```
524
525
## Error Handling
526
527
### Event Processing Errors
528
529
```java
530
import io.micronaut.context.event.EventListener;
531
import jakarta.inject.Singleton;
532
533
@Singleton
534
public class RobustEventHandler {
535
536
@EventListener
537
public void onOrderEvent(OrderEvent event) {
538
try {
539
processOrder(event.getOrder());
540
} catch (PaymentException e) {
541
System.err.println("Payment processing failed for order: " + event.getOrder().getId());
542
handlePaymentFailure(event.getOrder(), e);
543
} catch (InventoryException e) {
544
System.err.println("Inventory update failed for order: " + event.getOrder().getId());
545
handleInventoryFailure(event.getOrder(), e);
546
} catch (Exception e) {
547
System.err.println("Unexpected error processing order event: " + e.getMessage());
548
handleGenericFailure(event.getOrder(), e);
549
}
550
}
551
552
private void handlePaymentFailure(Order order, PaymentException e) {
553
// Specific payment failure handling
554
}
555
556
private void handleInventoryFailure(Order order, InventoryException e) {
557
// Specific inventory failure handling
558
}
559
560
private void handleGenericFailure(Order order, Exception e) {
561
// Generic error handling
562
}
563
}
564
```
565
566
## Implementation Classes
567
568
### DefaultApplicationEventPublisher
569
570
Default implementation of ApplicationEventPublisher.
571
572
```java { .api }
573
public class DefaultApplicationEventPublisher<T> implements ApplicationEventPublisher<T> {
574
public DefaultApplicationEventPublisher(BeanContext beanContext);
575
576
@Override
577
public void publishEvent(T event);
578
579
@Override
580
public Future<Void> publishEventAsync(T event);
581
}
582
```
583
584
### ApplicationEvent
585
586
Base class for all application events.
587
588
```java { .api }
589
public abstract class ApplicationEvent extends EventObject {
590
private final long timestamp;
591
592
public ApplicationEvent(Object source);
593
public final long getTimestamp();
594
}
595
```