Spring AOP: Pointcut Expressions & Advice Types
- Last updated
- Prerequisites:
- Spring bean lifecycle and proxies
- Understanding of cross-cutting concerns (logging, transactions)
- spring
- aop
- pointcut
- aspect
- advice
- proxy
- aspectj
Read at your depth
The practical view
Spring AOP lets you add behavior (advice) to methods matching a rule (pointcut) without modifying them. Common uses: logging, transactions, auditing, security. Define an @Aspect class with @Before, @After, @AfterReturning, @AfterThrowing, or @Around advice. Pointcut expressions select methods: execution(* com.example.service.*.*(..)), within, @annotation, @within, bean. Example: @Around("@annotation(LogExecution)") around any method annotated @LogExecution. Spring AOP uses proxies (JDK dynamic for interfaces, CGLIB for classes), so it only intercepts calls through the proxy — self-invocation is not intercepted.
The same idea in other frameworks
N/A — server-side Spring concern.N/A — Spring concern.N/A — Spring concern.N/A — Spring concern.Legacy vs modern
Manual logging in every method vs @Around aspect
Copy-pasting timing/logging code into every method is repetitive and drifts; an aspect applies it uniformly to every matched method.
public Order placeOrder(...) {
long start = System.nanoTime(); // ❌ OLD: repeated in every method
try { ... } finally {
log.info("placeOrder took {}ms", (System.nanoTime() - start) / 1_000_000);
}
}@Aspect
@Component
public class TimingAspect {
@Around("execution(* com.example.service.*.*(..))") // ✅ NEW
public Object time(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
try { return pjp.proceed(); }
finally { log.info("{} took {}ms", pjp.getSignature().getName(), (System.nanoTime() - start) / 1_000_000); }
}
}Interview gotchas
Context
This is the defining Spring AOP gotcha — interviewers want the proxy model explanation and the concrete limitations (public methods, self-calls, final classes) that come with proxy-based AOP.
The mechanical answer
Spring AOP works by wrapping the target bean in a proxy; only method calls that go THROUGH the proxy are intercepted. When a method on the same bean calls another advised method directly (this.someMethod()), the call executes on the raw target object — the proxy is bypassed, so the advice (and @Transactional/@Async) does not run. Additional limitations of proxy-based AOP: only PUBLIC method invocations through the proxy are advised (private/protected calls and calls on the raw object are not); CGLIB cannot proxy final classes or final methods; JDK dynamic proxies only work for interface-declared methods. Workarounds: inject the proxy into itself (self-injection), move the advised method to another bean, or use AspectJ compile-time/load-time weaving for full interception.
Trap
The naive answer is 'Spring AOP intercepts every method call'. It intercepts proxy-mediated public calls only. Another trap: thinking @Transactional inside the same bean is exempt — it's exactly the self-invocation case that silently skips the transaction. Candidates who explain the proxy boundary and the self-injection fix show real Spring experience.
Context
This tests the advice semantics — interviewers want to know what @Around can do that the others can't (controlling the invocation, changing the return value, handling exceptions) and when the simpler types suffice.
The mechanical answer
@Around is the most powerful: it wraps the method call entirely. You control whether and when the target runs (pjp.proceed()), can change the arguments, transform or replace the return value, catch exceptions, and short-circuit (not call proceed at all). The other advice types are non-controlling hooks: @Before runs before the method (can't stop it or change the result), @After runs after (like finally, regardless of outcome), @AfterReturning runs only after a successful return and can inspect the return value, @AfterThrowing runs only after an exception. Choose @Around when you need to modify behavior (timing, retries, caching, transaction boundaries); use the simpler types for pure observation (logging, auditing, validation) where you don't need to alter the invocation.
Trap
The naive answer is 'use @Around for everything because it's most powerful'. Overusing @Around makes aspects harder to read and easier to break (forgetting pjp.proceed() swallows the call). Another trap: thinking @AfterReturning can change the return value — it can only read it (returning a different value from an @AfterReturning advice has no effect in Spring AOP). Candidates who map each advice to its control level show real understanding.