Events and Interceptors

Back

Loading concept...

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:

  1. Tell other parts something happened (Events)
  2. Watch for messages from others (Observers)
  3. 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:

  1. @Inject Event<OrderPlaced> → Get a megaphone 📢
  2. 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 others
  • ctx.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! 🚀

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.