How Spring AOP Works (Proxy, JDK vs CGLIB)
Fast recall
Quick revision
Core answer
spring.aop.proxy-target-class=true). The proxy is transparent to callers but means self-invocation (calling a method via `this`) bypasses the proxy and skips advice — the most common AOP debugging scenario. Spring AOP is deliberately a subset of AspectJ — proxy-based, method-level only, public methods on Spring-managed beans; in exchange, you get zero build-time overhead and no special classloader.Whether the candidate understands why @Transactional breaks on internal calls and can explain the proxy-based mechanism behind most Spring magic.
Thinking AOP works through bytecode weaving in all cases; not knowing the self-invocation limitation or the difference between JDK proxy (interface-based) and CGLIB (class subclassing).
Distinguish Spring AOP (proxy-based, method-level only) from AspectJ (full compile/load-time weaving, field-level join points) and explain when you would reach for AspectJ.
Interview-ready guide
Interview answer
Spring AOP is proxy-based — not bytecode weaving.
- When you annotate a method with
@Transactionalor@Cacheable, Spring does not modify your code; it wraps your bean in a proxy object that intercepts external method calls and applies the aspect before or after delegating to the original. - Spring AOP is deliberately a subset of AspectJ — it borrows the annotation syntax and pointcut language but restricts join points to public method execution on Spring-managed beans.
How the proxy is created
- During
postProcessAfterInitialization,AbstractAutoProxyCreatorscans each bean for applicable advisors - If any advisor matches, it returns a proxy instead of the original bean
- JDK dynamic proxy: requires the bean to implement an interface; Spring Boot 2.0+ defaults to CGLIB even when interfaces exist because developers who inject the concrete class hit startup failures with JDK proxies
- CGLIB proxy: generates a runtime subclass of the concrete class; can proxy any public method but not
finalclasses orfinalmethods
The self-invocation trap
this.method()refers to the original object, not the proxy — the aspect never fires- Root cause of every my
@Transactionalis not working bug where one method calls another transactional method on the same class - Three fixes: move the called method to a separate bean (cleanest); inject the bean into itself and call through
self.method()(pragmatic); switch to AspectJ weaving (nuclear)
Advice types
@Before,@AfterReturning,@AfterThrowing,@Afterare observational — react to the invocation but cannot change its flow@Aroundis the powerful one — receives aProceedingJoinPoint, can short-circuitproceed(), transform arguments, catch exceptions;@Transactional,@Cacheable,@Asyncare all@Aroundunder the hood- When multiple aspects match the same join point, Spring orders them by
@Order(lower value = outermost wrapper)
Spring AOP vs AspectJ
- Spring AOP: proxy-based, public methods only, no self-invocation, zero build overhead, stack traces stay normal
- AspectJ: compile-time or load-time bytecode weaving, any method including private and final, self-invocation works, field-access and constructor join points available — use only when proxy limitations are a real constraint
How Spring AOP Works (Proxy, JDK vs CGLIB)
Direct Answer
Spring AOP creates proxies that wrap target beans and intercept method calls to apply cross-cutting concerns like transactions or caching. It uses JDK dynamic proxies when the bean implements at least one interface, and CGLIB subclass proxies when no interface is present (Spring Boot 2.0+ defaults to CGLIB even with interfaces, controlled by spring.aop.proxy-target-class=true). The proxy is transparent to callers but means self-invocation (calling a method via this) bypasses the proxy and skips advice — the most common AOP debugging scenario. Spring AOP is deliberately a subset of AspectJ — proxy-based, method-level only, public methods on Spring-managed beans; in exchange, you get zero build-time overhead and no special classloader.
Common Mistakes & Pitfalls
Thinking AOP works through bytecode weaving in all cases; not knowing the self-invocation limitation or the difference between JDK proxy (interface-based) and CGLIB (class subclassing).
Check your understanding
Answer these without looking back at the article.
- Why does @Transactional not work when called from the same class?
- What is the difference between Spring AOP and full AspectJ weaving?
- When would you choose proxyTargetClass=true?
- How do you order multiple aspects that match the same join point?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.