Question 16 · Section 5

What is Aspect, Advice, Pointcut, Join Point?

4. @Order -> execution order 5. getArgs() -> array copy, be careful 6. @Around -> do not forget proceed()

Language versions: English Russian Ukrainian

Junior Level

AOP Terms:

Term What it means Example
Join Point Specific method to intercept UserService.save()
Pointcut Rule WHERE to apply “All methods with @Transactional”
Advice WHAT to do (logic) “Log execution time”
Aspect Module combining Pointcut + Advice Logging class
@Aspect
@Component
public class LoggingAspect {  // Aspect

    @Around("@annotation(LogTime)")  // Pointcut
    public Object logTime(ProceedingJoinPoint pjp) {  // Advice
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();  // Join Point call
        } finally {
            log.info("Completed in " + (System.currentTimeMillis() - start) + "ms");
        }
    }
}

Middle Level

Types of Advice

@Before("pointcut")
public void before() { }  // Before method

@After("pointcut")
public void after() { }  // After method (always)
// @After = like finally, executes ALWAYS (on success and on exception)
// @AfterReturning = only if method completed SUCCESSFULLY (no exceptions)

@AfterReturning(pointcut = "pc", returning = "result")
public void afterReturn(Object result) { }  // After successful

@AfterThrowing(pointcut = "pc", throwing = "ex")
public void afterThrow(Exception ex) { }  // On exception

@Around("pointcut")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    // Before
    Object result = pjp.proceed();  // Method call
    // After
    return result;
}

Static vs Dynamic Pointcuts

Static (fast):
  -> Checked once during bean creation
  -> By annotations, signature, method name

Dynamic (slow):
  -> Checked on EVERY call
  -> By argument values
  -> Overhead on each check

Senior Level

JoinPoint Information

@Around("pointcut")
public Object log(ProceedingJoinPoint pjp) {
    pjp.getSignature();       // Method signature
    pjp.getArgs();            // Arguments (array copy!)
    pjp.getTarget();          // Original object
    pjp.getThis();            // Proxy

    // getArgs() creates a copy of the array -> GC pressure (noticeable only on very frequent aspect calls - hundreds of thousands per second - or with large argument arrays).
}

Aspect = Singleton

// Aspect is Singleton by default!
// -> Do NOT store state in aspect fields!
@Aspect
public class MetricsAspect {
    // Not thread-safe!
    private int callCount = 0;

    // ThreadLocal
    private ThreadLocal<Long> startTime = new ThreadLocal<>();
}

Pipeline

ReflectiveMethodInvocation:
  1. Finds all Advisors
  2. Filters by Pointcut
  3. Sorts by @Order
  4. Creates List<MethodInterceptor>
  5. Recursively goes through all
  6. Calls target.invoke()

Production Experience

Real scenario: Around without proceed

@Around("@annotation(Cacheable)")
public Object cache(ProceedingJoinPoint pjp) {
    if (cache.has(key)) return cache.get(key);
    // Forgot pjp.proceed()!
    // -> Method is NEVER called!
}

Best Practices

  1. Static Pointcut -> faster
  2. Narrow Pointcut -> less overhead. The more precise the pointcut, fewer beans will be proxied and fewer checks on each method call.
  3. No state in aspects (Singleton!)
  4. @Order -> execution order
  5. getArgs() -> array copy, be careful
  6. @Around -> do not forget proceed()

Summary for Senior

  • Join Point = call context
  • Pointcut = method filter
  • Advice = injection type
  • Aspect = logical module
  • Pipeline = interceptor chain
  • Aspect = Singleton -> no state
  • proceed() -> required in @Around

Interview Cheat Sheet

Must know:

  • Join Point = specific method, Pointcut = filter (where), Advice = logic (what), Aspect = module
  • 5 Advice types: @Before, @After, @AfterReturning, @AfterThrowing, @Around
  • @After executes ALWAYS (like finally), @AfterReturning - only on success
  • Static Pointcuts checked once during bean creation, dynamic - on each call
  • Aspect = Singleton -> do NOT store state in fields, use ThreadLocal
  • pjp.proceed() required in @Around - otherwise method will not be called
  • Pipeline: ReflectiveMethodInvocation filters Advisors by Pointcut, sorts by @Order, goes recursively
  • getArgs() creates array copy -> GC pressure on frequent calls

Common follow-up questions:

  • @After vs @AfterThrowing - when to use which? -> @After = resource cleanup always, @AfterThrowing = error handling
  • Why is aspect = Singleton? -> Spring registers aspects as regular beans (Singleton by default)
  • Static vs dynamic Pointcut - which to choose? -> Static is faster, dynamic is more flexible; prefer static
  • What happens if you forget pjp.proceed()? -> Original method will not execute at all

Red flags (DO NOT say):

  • “@After only on success” - that is @AfterReturning
  • “Aspect storing state is fine” - Singleton, not thread-safe
  • “Dynamic Pointcut is faster” - opposite, checked every time
  • “getArgs() returns the original array” - creates a copy

Related topics:

  • [[15. What is AOP Aspect-Oriented Programming]]
  • [[13. What is a proxy in Spring]]
  • [[17. What does the Transactional annotation do]]
  • [[18. Why Transactional does not work with self-invocation]]