CDI Events and Interceptors: The Secret Messengers and Guards of Your App
Imagine your app is a busy school. Events are like passing notes between friends. Interceptors are like hall monitors who check every kid before they enter class.
🎯 The Big Picture
In Jakarta EE, your code often needs to:
- Tell other parts something happened (Events)
- Watch for messages from others (Observers)
- Check or modify things before they happen (Interceptors)
Think of it like a news system inside your app!
📬 CDI Events: Passing Notes in Class
What Are CDI Events?
An event is a message your code sends out. It’s like shouting “Lunch time!” and anyone who cares can listen.
Real Life Example:
- When you order pizza 🍕, the shop fires an “order received” event
- The kitchen listens and starts making pizza
- The delivery driver listens and gets ready
How to Fire an Event
@Inject
Event<OrderPlaced> orderEvent;
public void placeOrder(Order order) {
// Save the order first
saveOrder(order);
// Now tell everyone!
orderEvent.fire(new OrderPlaced(order));
}
What’s happening:
@Inject Event<OrderPlaced>→ Get a megaphone 📢orderEvent.fire(...)→ Shout your message!
The Event Object
Your event is just a simple class:
public class OrderPlaced {
private Order order;
public OrderPlaced(Order order) {
this.order = order;
}
public Order getOrder() {
return order;
}
}
It’s like a note with information inside!
👀 CDI Event Observers: Listening for Notes
What Are Observers?
An observer is code that waits for a specific message. When that message arrives, it wakes up and does something!
graph TD A["🔔 Event Fired"] --> B["Observer 1 Wakes Up"] A --> C["Observer 2 Wakes Up"] A --> D["Observer 3 Wakes Up"] B --> E["Sends Email"] C --> F["Updates Dashboard"] D --> G["Logs to File"]
How to Create an Observer
public class EmailService {
public void onOrderPlaced(
@Observes OrderPlaced event) {
Order order = event.getOrder();
sendEmail(order.getCustomerEmail(),
"Your order is confirmed!");
}
}
The magic word is @Observes!
Multiple Observers
You can have many listeners for the same event:
// In EmailService
public void sendConfirmation(
@Observes OrderPlaced event) {
// Send email
}
// In InventoryService
public void reduceStock(
@Observes OrderPlaced event) {
// Update inventory
}
// In AnalyticsService
public void trackSale(
@Observes OrderPlaced event) {
// Record statistics
}
All three run automatically when the event fires!
⚡ Async Events: Don’t Wait, Keep Going!
The Problem with Regular Events
Regular events are synchronous. Your code waits for ALL observers to finish.
Fire Event → Wait → Wait → Wait → Continue
↳ Observer 1 working...
↳ Observer 2 working...
↳ Observer 3 working...
This can be slow! 🐢
Async to the Rescue!
With async events, you fire and forget:
@Inject
Event<OrderPlaced> orderEvent;
public void placeOrder(Order order) {
saveOrder(order);
// Fire and don't wait!
orderEvent.fireAsync(new OrderPlaced(order));
// Continue immediately!
return "Order placed!";
}
Notice: fireAsync() instead of fire()
Async Observer
public void onOrderPlaced(
@ObservesAsync OrderPlaced event) {
// This runs in a separate thread
sendSlowEmail(event.getOrder());
}
Notice: @ObservesAsync instead of @Observes
When to Use Async?
| Use Async When… | Use Sync When… |
|---|---|
| Sending emails | Need immediate result |
| Logging to files | Order matters |
| External API calls | Must complete before continuing |
| Heavy calculations | Simple, fast operations |
🛡️ CDI Interceptors: The Hall Monitors
What Are Interceptors?
An interceptor is code that runs around your method. It can:
- Run code BEFORE your method
- Run code AFTER your method
- Even SKIP your method entirely!
graph TD A["Method Called"] --> B["Interceptor Before"] B --> C["Your Method Runs"] C --> D["Interceptor After"] D --> E["Return Result"]
Real Life Examples
| Interceptor Does | Like In Real Life |
|---|---|
| Logging | Security camera recording |
| Security check | Bouncer at club entrance |
| Performance timing | Stopwatch on a race |
| Transaction management | Bank recording transfers |
Creating an Interceptor
Step 1: Create the Interceptor Class
@Interceptor
public class LoggingInterceptor {
@AroundInvoke
public Object logMethod(
InvocationContext ctx) throws Exception {
// BEFORE the method
System.out.println("Starting: "
+ ctx.getMethod().getName());
long start = System.currentTimeMillis();
// RUN the actual method
Object result = ctx.proceed();
// AFTER the method
long time = System.currentTimeMillis() - start;
System.out.println("Finished in " + time + "ms");
return result;
}
}
Key Parts:
@Interceptor→ This is an interceptor@AroundInvoke→ This method wraps othersctx.proceed()→ Run the actual method
🏷️ CDI Interceptor Bindings: Choosing Who Gets Monitored
What Are Interceptor Bindings?
A binding is like a sticker you put on methods. Methods with the sticker get intercepted!
Creating a Binding
Step 1: Create the Annotation
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Logged {
}
This creates a sticker called @Logged
Step 2: Connect Interceptor to Binding
@Interceptor
@Logged // ← Uses our sticker!
public class LoggingInterceptor {
@AroundInvoke
public Object logMethod(
InvocationContext ctx) throws Exception {
System.out.println("Method called!");
return ctx.proceed();
}
}
Step 3: Put Sticker on Methods
public class OrderService {
@Logged // ← This method gets logged
public void placeOrder(Order order) {
// Business logic
}
// This method is NOT logged
public int getOrderCount() {
return count;
}
}
Enable the Interceptor
Add to beans.xml:
<beans>
<interceptors>
<class>
com.app.LoggingInterceptor
</class>
</interceptors>
</beans>
Multiple Bindings
You can stack multiple interceptors:
@Logged
@Timed
@Secured
public void importantMethod() {
// This method has 3 interceptors!
}
graph TD A["Call importantMethod"] --> B["@Secured Check"] B --> C["@Logged Records"] C --> D["@Timed Starts Timer"] D --> E["Method Runs"] E --> F["@Timed Stops Timer"] F --> G["@Logged Finishes"] G --> H["Return"]
🎮 Complete Example: Pizza Shop
Let’s put it all together!
The Events
// Event when order is placed
public class PizzaOrdered {
private String type;
private String customer;
// constructor, getters...
}
// Event when pizza is ready
public class PizzaReady {
private String orderId;
// constructor, getters...
}
The Order Service
public class PizzaShop {
@Inject
Event<PizzaOrdered> orderEvent;
@Logged
@Timed
public String orderPizza(String type,
String customer) {
// Fire event - kitchen listens!
orderEvent.fire(
new PizzaOrdered(type, customer));
return "Order received!";
}
}
The Observers
// Kitchen starts cooking
public class Kitchen {
@Inject
Event<PizzaReady> readyEvent;
public void startCooking(
@Observes PizzaOrdered order) {
System.out.println("Making "
+ order.getType() + " pizza!");
// When done, fire ready event
readyEvent.fire(new PizzaReady(orderId));
}
}
// Delivery gets notified
public class DeliveryTeam {
public void prepareDelivery(
@ObservesAsync PizzaReady event) {
System.out.println("Pizza "
+ event.getOrderId() + " is ready!");
// Prepare for delivery
}
}
The Interceptor
@Interceptor
@Timed
public class TimingInterceptor {
@AroundInvoke
public Object time(InvocationContext ctx)
throws Exception {
long start = System.currentTimeMillis();
Object result = ctx.proceed();
long duration = System.currentTimeMillis() - start;
System.out.println(ctx.getMethod().getName()
+ " took " + duration + "ms");
return result;
}
}
🎯 Quick Summary
| Concept | What It Does | Key Annotation |
|---|---|---|
| CDI Events | Send messages | Event<T>.fire() |
| Event Observers | Listen for messages | @Observes |
| Async Events | Send without waiting | fireAsync() |
| Async Observers | Listen in background | @ObservesAsync |
| Interceptors | Code that wraps methods | @Interceptor |
| Interceptor Bindings | Choose what to intercept | @InterceptorBinding |
💡 Remember This!
Events = Passing notes 📝 Observers = Friends reading notes 👀 Async = Don’t wait for reply ⚡ Interceptors = Hall monitors 🛡️ Bindings = Stickers on doors 🏷️
You now understand how parts of your app can talk to each other and how to add cross-cutting concerns without cluttering your code. You’ve got this! 🚀
