Aspect-Oriented Programming is the part of Spring that quietly powers @Transactional, @Cacheable and Spring Security's method checks — which is exactly why interviewers ask about it. They want to know whether you understand the proxy mechanism underneath those annotations, not just that they exist. This set covers the AOP questions asked in intermediate Spring Boot rounds, with correct terminology and a working @Around aspect.
What is AOP and what problem does it solve?
AOP separates cross-cutting concerns — logic that appears across many unrelated methods, like logging, security, transactions and metrics — from the business logic itself. You write the concern once in an aspect and declare where it applies, instead of copying boilerplate into every method.
The classic example is transaction management. Without AOP, every service method would open a transaction, commit on success and roll back on failure — the same six lines everywhere. With AOP, @Transactional marks the method and an aspect wraps that behaviour around it. Your method contains only business logic; the concern is applied from the outside.
The value is not just less code. It is that the concern is defined in one place, so changing how you log or how transactions behave is a single edit rather than a search-and-replace across the codebase.
Explain the core AOP terminology.
An aspect is the module bundling a cross-cutting concern. A join point is a point in execution where advice can apply — in Spring AOP, always a method execution. A pointcut is an expression selecting which join points to advise. Advice is the action taken at a matched join point. Weaving is linking aspects into the target — in Spring, at runtime via proxies.
The relationship is the thing to make crisp: a pointcut picks join points, and advice is what runs at the picked ones; the aspect contains both. Weaving is how they get connected.
Interview note: A frequent slip is conflating join point and pointcut. Join point = a candidate location (every method is one); pointcut = the filter that selects some of them. Say it that way and the follow-up disappears.
What are the types of advice in Spring AOP?
Five: @Before (runs before the method), @AfterReturning (after a successful return, can read the returned value), @AfterThrowing (when the method throws), @After (a finally block — runs on either outcome), and @Around (wraps the entire call and decides whether, when and with what arguments the target runs).
@Aspect
@Component
public class AuditAspect {
@Before("execution(* com.app.service.*.*(..))")
public void before(JoinPoint jp) { /* before every service method */ }
@AfterReturning(pointcut = "execution(* com.app.service.*.*(..))",
returning = "result")
public void afterReturning(Object result) { /* inspect return value */ }
@AfterThrowing(pointcut = "execution(* com.app.service.*.*(..))",
throwing = "ex")
public void afterThrowing(Exception ex) { /* log failures */ }
}
@Around is the most powerful because it holds a ProceedingJoinPoint and controls the call. The others are convenient shorthands for the common cases where you only need to observe.
Interview note: Follow-up: "which advice can prevent the method from running?" Only
@Around— by choosing not to callproceed().@Beforeruns before but cannot stop the method (short of throwing an exception).
How do you declare an aspect?
Annotate a class with @Aspect to mark it as holding advice, and with @Component (or register it as a bean) so Spring finds it. Spring Boot auto-configures AOP when spring-boot-starter-aop is on the classpath, so no extra @EnableAspectJAutoProxy is needed in a Boot app.
@Aspect alone does nothing — it is an AspectJ annotation that the Spring container recognises, but the class still has to be a Spring bean for its advice to be applied. Forgetting @Component is the most common "my aspect never fires" bug, and interviewers like asking why an aspect might silently not run.
What does a pointcut expression look like?
The most common designator is execution(...). execution(* com.app.service.*.*(..)) means: any return type (*), any class in com.app.service, any method name (.*), any arguments ((..)). You can also match by annotation with @annotation(...) or by type with within(...).
@Aspect
@Component
public class LoggingAspect {
// Reusable named pointcut
@Pointcut("@annotation(com.app.Loggable)")
public void loggableMethods() {}
@Before("loggableMethods()")
public void logCall(JoinPoint jp) {
System.out.println("Calling " + jp.getSignature().getName());
}
}
Naming a pointcut with @Pointcut and reusing it keeps expressions DRY and readable. The @annotation form is especially interview-friendly because it shows how custom annotations like a @Loggable marker get their behaviour.
Interview note: Trap: "does
execution(* *(..))advise everything, including your aspect?" It can match far more than intended, including framework beans — over-broad pointcuts cause performance and recursion surprises. Interviewers like candidates who scope pointcuts tightly.
How does proxy-based AOP work — JDK dynamic proxy versus CGLIB?
Spring wraps the target bean in a proxy. Callers get the proxy, not the real object; the proxy runs the advice, then delegates to the target. If the bean implements an interface, Spring can use a JDK dynamic proxy (interface-based); otherwise it uses CGLIB, which creates a runtime subclass of the target class.
The practical differences: JDK proxies require an interface and proxy only interface methods; CGLIB works without an interface but cannot subclass final classes or override final/private methods. Spring Boot sets proxy-target-class=true by default, so it uses CGLIB even when interfaces exist — this avoids surprises where injecting the concrete type fails.
@Around("execution(* com.app.service.*.*(..))")
public Object time(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
try {
return pjp.proceed(); // call the real method
} finally {
long ms = (System.nanoTime() - start) / 1_000_000;
System.out.println(pjp.getSignature() + " took " + ms + " ms");
}
}
ProceedingJoinPoint.proceed() is the hinge of @Around: everything before it runs before the method, everything after runs after, and you can even alter the arguments passed to proceed(Object[]).
Why does self-invocation bypass the aspect?
Because the advice lives on the proxy, not on the target object. When one method in a bean calls another method of the same bean with this.other(), the call goes directly to the target and never passes through the proxy — so no advice runs. This is the single most-tested AOP gotcha.
@Service
public class ReportService {
@Transactional
public void generate() {
save(); // self-call: @Transactional on save() is IGNORED
}
@Transactional
public void save() { /* ... */ }
}
Here save()'s transaction never starts when reached via generate(), because the internal call skips the proxy. The exact same mechanism silently disables @Cacheable, @Async and custom aspects on self-invoked methods.
The fixes to name: split the two methods into separate beans so the call crosses a proxy boundary; inject a self-reference to the proxied bean and call through it; or move to AspectJ compile-time/load-time weaving, which weaves into the class itself and does not rely on a proxy.
Interview note: Follow-up: "so why does
@Transactionalon a public method called from a controller work fine?" Because that call comes from outside the bean, through the proxy. Self-invocation only breaks calls that originate inside the same object.
What is Spring AOP unable to do that AspectJ can?
Spring AOP only advises method executions on Spring-managed beans, and only through proxies. It cannot advise constructors, field access, final methods, private methods, or objects Spring did not create. AspectJ, with compile-time or load-time weaving, can do all of that because it modifies the bytecode directly.
For the vast majority of applications, Spring AOP's method-level proxying is enough — it is exactly what transactions, caching and security need. AspectJ is reserved for cases that genuinely require advising things a proxy cannot reach, at the cost of a weaving step in the build or a load-time agent.
Interview note: Trap: "Spring uses AspectJ, so it can advise fields." Spring reuses AspectJ's annotations and pointcut language, but its default runtime is proxy-based and limited to methods. The annotation syntax being shared does not grant AspectJ's full weaving power.
What interviewers really test
AOP questions reward candidates who can connect the abstract vocabulary to the concrete proxy mechanism, then to the annotations built on it. The strongest signal is explaining the self-invocation limitation unprompted, because it proves you understand why @Transactional sometimes does nothing rather than treating annotations as magic. Interviewers also listen for tightly scoped pointcuts and for knowing that Spring Boot defaults to CGLIB.
To prepare, write the @Around timing aspect above, confirm it fires on external calls, then reproduce the self-invocation bug and watch the advice not run — seeing it once fixes the concept permanently. The Spring Boot learning path covers the surrounding container concepts, and this pairs naturally with the dependency injection interview set, since proxies and DI both flow from how the container manages beans. A mock interview is the fastest way to practise explaining proxies out loud under follow-up pressure.
Frequently Asked Questions
What problem does Spring AOP solve?
What are the five types of advice in Spring AOP?
Why does calling an advised method from within the same class not trigger the aspect?
What is the difference between JDK dynamic proxy and CGLIB?
Is Spring AOP the same as AspectJ?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Discover CodeBegun's Java Full Stack track

