Interview Prep Zoneby CuriouserLabs

Question 3 of 5 · senior level

Write a production-quality @Around aspect. What do pointcut expressions look like, and what mistakes do you watch for?

🎤 Say this first

An @Around advice receives a ProceedingJoinPoint; you must call proceed() (or deliberately not), return its result, and let exceptions propagate — swallowing them or forgetting the return value corrupts callers. Prefer annotation-driven pointcuts (@annotation(…)) over broad execution(* com.app..*(..)) package sweeps: explicit opt-in beats accidental interception. Keep aspects stateless, fast, and exception-safe — an aspect that throws takes the business call down with it.

The full picture

A timing aspect done properly
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME)
public @interface Timed { String value(); }

@Aspect @Component
public class TimingAspect {
    private final MeterRegistry metrics;
    TimingAspect(MeterRegistry metrics) { this.metrics = metrics; }

    @Around("@annotation(timed)")                 // binds the annotation instance
    public Object time(ProceedingJoinPoint pjp, Timed timed) throws Throwable {
        long t0 = System.nanoTime();
        String outcome = "success";
        try {
            return pjp.proceed();                 // 1. ALWAYS return proceed()'s value
        } catch (Throwable t) {
            outcome = "error";                    // 2. observe, DON'T swallow
            throw t;
        } finally {                               // 3. aspect never throws from here
            metrics.timer(timed.value(), "outcome", outcome)
                   .record(System.nanoTime() - t0, TimeUnit.NANOSECONDS);
        }
    }
}
  • Pointcut vocabulary: execution(pattern) method matching; within(type) lexical scope; @annotation / @within annotation-driven; bean(name) Spring-specific; combine with && || !. Reusable via @Pointcut methods.
  • Advice types: @Before, @AfterReturning, @AfterThrowing, @After (finally), @Around — use the least powerful that works; @Around everywhere is a smell.
  • Mistake checklist: forgetting proceed() (target never runs), returning null from Object-typed advice on primitive-returning methods (NPE on unboxing), doing blocking I/O in a hot-path aspect, wide pointcuts catching Spring's own infrastructure beans.

🔄 Likely follow-up questions

  • When would @AfterReturning beat @Around?
  • How do you unit-test an aspect in isolation? (AspectJProxyFactory)
  • How do argument-binding pointcuts (args, this, target) work?