2V0-72.22 Exam Guide
Spring AOP on the 2V0-72.22: What Gets Tested and Why It Trips People Up
AOP is one of the domains where candidates lose the most points. The questions are not about knowing what AOP is — they test whether you understand how Spring's proxy-based implementation behaves in specific scenarios, and the edge cases that break assumptions.
Spring AOP is proxy-based, not bytecode manipulation
This is the most important thing to internalize. Spring AOP wraps your bean in a proxy. The proxy intercepts method calls and applies advice. Full AspectJ compiles advice into the bytecode directly — Spring does not. The exam exploits this difference repeatedly.
Spring uses one of two proxy mechanisms depending on the bean:
- JDK dynamic proxy— used when the bean implements at least one interface. The proxy implements the same interface(s).
- CGLIB proxy— used when there is no interface, or when
proxyTargetClass = trueis set. Creates a subclass at runtime.
CGLIB cannot subclass a final class. If a question shows a final class with an aspect applied — no proxy can be created, and AOP silently does nothing.
The self-invocation trap
This is the most commonly tested AOP scenario. When a bean calls one of its own methods internally, the call goes directly to this — bypassing the proxy entirely. No advice runs.
@Service
public class OrderService {
public void placeOrder() {
// This calls the real method directly, NOT through the proxy.
// Any @Transactional or custom advice on processPayment() will NOT fire.
this.processPayment();
}
@Transactional
public void processPayment() {
// ...
}
}The fix is to inject the bean into itself (@Autowired self-reference) or to restructure the code so the call goes through the proxy. The exam will show you code like the above and ask why the advice does not apply — the answer is always self-invocation.
Advice types — know the exact behavior of each
The exam distinguishes between advice types through scenario questions: “Which advice type can modify the return value?” or “Which advice fires even when an exception is thrown?”
| Advice | When it runs | Can stop execution? | Can modify return? |
|---|---|---|---|
| @Before | Before method | Only by throwing | No |
| @AfterReturning | After successful return | No | No (can read it) |
| @AfterThrowing | When exception propagates | No | No |
| @After | Always (like finally) | No | No |
| @Around | Wraps the method | Yes (skip proceed()) | Yes |
The critical @Around trap: if your advice method does not call joinPoint.proceed(), the real method never executes. For non-void methods, the advice returns null (or the zero value for primitives). The exam will show this and ask what happens.
@Around("execution(* com.example.service.*.*(..))")
public Object logDuration(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // must call this
long elapsed = System.currentTimeMillis() - start;
log.info("Took {}ms", elapsed);
return result; // must return this
}Pointcut expressions — the most-tested syntax
Most exam questions on AOP involve reading or writing a pointcut expression. Focus on these designators:
// execution: matches method signatures — the most common
execution(* com.example.service.*.*(..)) // any method in service package (not subpackages)
execution(* com.example.service..*.*(..)) // any method in service and subpackages
execution(public * *(..)) // any public method anywhere
execution(* *Service.find*(String)) // find* methods on *Service beans taking a String
// within: matches all methods in a type or package (no method-level filtering)
within(com.example.service.*) // all types directly in the package
within(com.example.service..*) // all types in the package and subpackages
// @annotation: matches methods with a specific annotation
@annotation(org.springframework.transaction.annotation.Transactional)
// @within: matches all methods in types that have a specific class-level annotation
@within(org.springframework.stereotype.Service)
// bean: matches a named Spring bean (Spring-specific, not in AspectJ)
bean(orderService)
bean(*Service) // wildcard supportedKey distinction the exam tests: @annotation matches the annotation on the method. @within matches the annotation on the class. A method without @Transactional on a class annotated @Service is matched by @within(Service) but not by @annotation(Transactional).
Pointcuts can be combined:
// && (and), || (or), ! (not)
execution(* com.example.service.*.*(..)) && @annotation(Transactional)
execution(* com.example.*.*(..)) && !within(com.example.internal.*)Aspect ordering with @Order
When multiple aspects apply to the same join point, their order matters. @Order controls it — lower value means higher priority.
@Aspect
@Order(1) // runs first (outermost in the call stack)
@Component
public class SecurityAspect { ... }
@Aspect
@Order(2) // runs second
@Component
public class LoggingAspect { ... }For @Before: order 1 fires before order 2. For @After and @AfterReturning: order 1 fires after order 2 (it wraps the other, so it unwinds last). Think of it as nested try/finally blocks — the outermost aspect is the first to enter and the last to exit.
Configuration — what the exam expects you to know
@Configuration
@EnableAspectJAutoProxy // enables AOP proxy creation
// @EnableAspectJAutoProxy(proxyTargetClass = true) // force CGLIB for all beans
public class AppConfig { }
@Aspect // marks this as an aspect — NOT a bean by itself
@Component // still needed for Spring to pick it up
public class MyAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {} // reusable pointcut
@Before("serviceLayer()")
public void beforeService(JoinPoint joinPoint) {
// joinPoint.getSignature(), joinPoint.getArgs(), etc.
}
}Note: @Aspect alone does not register the class as a Spring bean. You still need @Component (or an explicit bean definition) for Spring to manage it. The exam tests this combination.
Quick reference: what the exam actually asks
- — A
finalclass with an aspect: AOP does not apply (CGLIB cannot subclass it) - —
this.method()inside a Spring bean: advice does not fire (self-invocation bypasses proxy) - —
@Aroundwithoutproceed(): original method never runs - — Private method with a pointcut: not intercepted (proxies can only intercept public methods)
- —
within(com.example.service.*)vswithin(com.example.service..*): single*excludes subpackages, double..*includes them - —
@annotationvs@within: method-level annotation vs class-level annotation - — Lower
@Ordervalue = higher priority = outermost aspect in the call stack
Practice AOP questions in exam conditions
PrepForge mock exams include AOP scenarios drawn from real exam patterns — timed, with full explanations.
Start a Mock Exam
