Interceptors

Back

Loading concept...

🛡️ Jakarta EE Interceptors: Your Code’s Secret Bodyguards

The Story: Meet the Security Team

Imagine you own a fancy restaurant. Every time a customer orders food, you want:

  • Someone to check if they’re a VIP (before cooking)
  • Someone to add a special garnish (after cooking)
  • Someone to log what was ordered (always)

You could tell every chef to do all this… OR you could hire a dedicated team that automatically handles these tasks for EVERY dish. No chef needs to know about it!

That’s exactly what Interceptors do in Jakarta EE! 🎯

They’re like invisible helpers that run code before, after, or around your main business logic—without changing that logic at all.


🎭 What Are Interceptors?

Simple Definition: Interceptors are special classes that “intercept” (catch) method calls and do something extra—like adding seasoning to every dish automatically.

Real-Life Examples:

  • 📝 Logging every action a user takes
  • ⏱️ Measuring how long operations take
  • 🔐 Checking permissions before sensitive operations
  • 💾 Starting/ending database transactions

🏗️ Interceptor Classes: Building Your Bodyguards

An Interceptor Class is just a regular Java class with special annotations that tell Jakarta EE: “Hey, I want to intercept stuff!”

The Simplest Interceptor

import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;

@Interceptor
public class LoggingInterceptor {

    @AroundInvoke
    public Object logMethod(InvocationContext ctx)
            throws Exception {

        System.out.println("BEFORE: " +
            ctx.getMethod().getName());

        Object result = ctx.proceed();

        System.out.println("AFTER: " +
            ctx.getMethod().getName());

        return result;
    }
}

What’s Happening Here?

graph TD A["Method Called"] --> B["Interceptor Catches It"] B --> C["🟡 BEFORE Logic Runs"] C --> D["ctx.proceed - Original Method Runs"] D --> E["🟢 AFTER Logic Runs"] E --> F["Result Returned"] style B fill:#ff6b6b,color:#fff style D fill:#4ecdc4,color:#fff

Key Parts:

Part What It Does
@Interceptor Marks this class as an interceptor
@AroundInvoke Says “run this method around business methods”
InvocationContext Gives you info about what was intercepted
ctx.proceed() Calls the original method

🎯 AroundInvoke: The Main Event

@AroundInvoke is the most common interceptor type. It wraps around regular business methods.

The Recipe

Every @AroundInvoke method must:

  1. Return Object
  2. Take InvocationContext as parameter
  3. Call ctx.proceed() (or throw exception)
  4. Throw Exception

Practical Example: Timing Methods

@Interceptor
public class TimingInterceptor {

    @AroundInvoke
    public Object measureTime(InvocationContext ctx)
            throws Exception {

        long start = System.currentTimeMillis();

        try {
            return ctx.proceed();
        } finally {
            long duration =
                System.currentTimeMillis() - start;
            System.out.println(
                ctx.getMethod().getName() +
                " took " + duration + "ms"
            );
        }
    }
}

What Can You Do in AroundInvoke?

Action Example
Read parameters ctx.getParameters()
Modify parameters ctx.setParameters(newParams)
Skip the method Don’t call proceed(), return value
Throw exception throw new SecurityException()
Modify result Change what proceed() returns

🏭 AroundConstruct: Watching Objects Being Born

@AroundConstruct intercepts when objects are created (constructors).

Why Would You Need This?

  • ✅ Validate that an object is created correctly
  • ✅ Log every object creation
  • ✅ Inject dependencies manually
  • ✅ Measure construction time

Example: Construction Logger

@Interceptor
public class ConstructionInterceptor {

    @AroundConstruct
    public void logConstruction(InvocationContext ctx)
            throws Exception {

        System.out.println("Creating: " +
            ctx.getConstructor()
               .getDeclaringClass()
               .getSimpleName()
        );

        ctx.proceed();

        System.out.println("Created successfully!");
    }
}

Key Difference from AroundInvoke

Feature AroundInvoke AroundConstruct
Returns Object void
Intercepts Methods Constructors
Get target ctx.getTarget() null (not created yet!)

Remember: In @AroundConstruct, the object doesn’t exist yet before proceed()!


⏰ AroundTimeout: Catching Timer Events

@AroundTimeout intercepts scheduled timer methods (like cron jobs in enterprise apps).

When Timers Fire

@Stateless
public class ReportGenerator {

    @Schedule(hour = "9", minute = "0")
    public void generateDailyReport() {
        // Runs every day at 9 AM
        System.out.println("Generating report...");
    }
}

Intercepting Timer Callbacks

@Interceptor
public class TimerInterceptor {

    @AroundTimeout
    public Object handleTimeout(InvocationContext ctx)
            throws Exception {

        System.out.println("⏰ Timer fired: " +
            ctx.getMethod().getName());

        try {
            return ctx.proceed();
        } catch (Exception e) {
            System.out.println("⚠️ Timer failed!");
            // Maybe retry or notify admin
            throw e;
        }
    }
}

Use Cases for AroundTimeout

Use Case What You’d Do
Monitoring Log when timers fire
Error handling Catch and handle timer failures
Metrics Track timer execution duration
Circuit breaker Skip if system is overloaded

📋 Interceptor Ordering: Who Goes First?

When you have multiple interceptors, the order matters! It’s like a security checkpoint with multiple guards.

The Problem

Interceptor A → Interceptor B → Interceptor C → Your Method

Which runs first? Second? Last?

Solution: @Priority

import jakarta.annotation.Priority;
import jakarta.interceptor.Interceptor;

@Interceptor
@Priority(100)
public class SecurityInterceptor {
    // Runs FIRST (lowest number)
}

@Interceptor
@Priority(200)
public class LoggingInterceptor {
    // Runs SECOND
}

@Interceptor
@Priority(300)
public class TimingInterceptor {
    // Runs THIRD (highest number)
}

How Priority Works

graph TD A["Method Call"] --> B["Priority 100<br/>SecurityInterceptor"] B --> C["Priority 200<br/>LoggingInterceptor"] C --> D["Priority 300<br/>TimingInterceptor"] D --> E["Actual Method"] E --> D D --> C C --> B B --> F["Return to Caller"] style B fill:#ff6b6b,color:#fff style C fill:#feca57,color:#000 style D fill:#48dbfb,color:#000 style E fill:#1dd1a1,color:#fff

Standard Priority Ranges

Range Purpose
1000-1999 Platform interceptors
2000-2999 Library interceptors
3000-3999 Application interceptors

Ordering Without @Priority

If you don’t use @Priority, use @Interceptors annotation to specify order:

@Interceptors({
    SecurityInterceptor.class,
    LoggingInterceptor.class,
    TimingInterceptor.class
})
public class MyService {
    // Interceptors run in the order listed
}

🔗 Binding Interceptors to Classes

Method 1: @Interceptors Annotation

import jakarta.interceptor.Interceptors;

@Interceptors(LoggingInterceptor.class)
public class OrderService {

    public void createOrder() {
        // Logging interceptor runs here
    }
}

Method 2: Interceptor Bindings (Cleaner!)

Step 1: Create a binding annotation

import jakarta.interceptor.InterceptorBinding;
import java.lang.annotation.*;

@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Logged {
}

Step 2: Bind interceptor to annotation

@Interceptor
@Logged
@Priority(2000)
public class LoggingInterceptor {
    @AroundInvoke
    public Object log(InvocationContext ctx)
            throws Exception {
        // logging logic
        return ctx.proceed();
    }
}

Step 3: Use the annotation

@Logged
public class OrderService {
    // All methods are now logged!
}

// Or on specific methods:
public class UserService {

    @Logged
    public void sensitiveOperation() {
        // Only this method is logged
    }
}

🎓 Complete Example: Building a Security System

Let’s build a real-world interceptor system for an e-commerce app:

// Binding annotation
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Secured {
}

// Security interceptor
@Interceptor
@Secured
@Priority(1000)
public class SecurityInterceptor {

    @AroundInvoke
    public Object checkSecurity(InvocationContext ctx)
            throws Exception {

        // Check if user is authenticated
        if (!isUserAuthenticated()) {
            throw new SecurityException(
                "Please log in first!"
            );
        }

        return ctx.proceed();
    }

    private boolean isUserAuthenticated() {
        // Check authentication logic
        return true;
    }
}

// Using it
@Secured
public class PaymentService {

    public void processPayment(Order order) {
        // This is now protected!
        System.out.println("Processing payment...");
    }
}

🧠 Quick Memory Tips

Type Remember As When It Runs
@AroundInvoke “Around methods” Before/after regular methods
@AroundConstruct “Around birth” When objects are created
@AroundTimeout “Around alarms” When timers fire
@Priority(n) “Queue number” Lower = runs first

🚀 Key Takeaways

  1. Interceptors = Invisible Helpers - They add behavior without changing your business code
  2. AroundInvoke is King - Used 90% of the time for cross-cutting concerns
  3. AroundConstruct for Creation - Watch objects being born
  4. AroundTimeout for Timers - Handle scheduled tasks
  5. Priority Matters - Lower numbers run first
  6. Bindings are Cleaner - Use custom annotations instead of @Interceptors

🎯 The Big Picture

graph TD subgraph "Your Code" A["Business Method"] end subgraph "Interceptor Chain" B["Security Check"] C["Transaction Start"] D["Logging"] E["Timing"] end Client --> B B --> C C --> D D --> E E --> A A --> E E --> D D --> C C --> B B --> Client style A fill:#1dd1a1,color:#fff style B fill:#ff6b6b,color:#fff style C fill:#feca57,color:#000 style D fill:#48dbfb,color:#000 style E fill:#a55eea,color:#fff

You’re now ready to build powerful, clean enterprise applications with interceptors! 🎉

The beauty is: your business code stays clean and focused, while interceptors handle all the repetitive cross-cutting concerns. That’s the Jakarta EE way!

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.