What is a Proxy in Spring?
4. AopUtils.getTargetClass() -> for reflection 5. Avoid final on proxied methods 6. Spring Boot -> CGLIB by default
Junior Level
Proxy is an intermediary object that Spring creates around your bean.
Simple analogy: A receptionist. You want to talk to the director, but first you go through the receptionist who checks your documents, records your appointment, etc.
@Service
public class UserService {
@Transactional // Spring will create a Proxy around this bean
public void save(User user) { }
}
// You receive not UserService, but a Proxy!
// Proxy -> opens transaction -> calls save() -> closes transaction
Why:
- Adds functionality without changing your code
- @Transactional, @Async, @Cacheable - all work through proxies
Middle Level
Two Types of Proxies
JDK Dynamic Proxy:
// Via interfaces
public interface UserService { void save(User user); }
// Spring creates:
class UserServiceProxy implements UserService {
private UserService target;
public void save(User user) {
// Before call
target.save(user);
// After call
}
}
CGLIB:
// Via inheritance
class UserServiceProxy extends UserService {
// Overrides methods
}
// Cannot proxy final classes/methods!
Spring Boot Defaults
Spring Boot 2.x/3.x: proxy-target-class=true by default, so CGLIB is always used, even if interfaces exist. Spring 1.x: JDK Dynamic Proxy by default.
Why:
-> Fewer type problems
-> Can inject by class, not by interface
Self-invocation
@Service
public class UserService {
public void outer() {
inner(); // @Transactional will NOT work!
}
@Transactional
public void inner() { }
}
// outer() -> inner() goes directly, around the proxy!
Senior Level
Interceptor Chain
Method call on proxy:
1. SecurityInterceptor -> permission check
2. TransactionInterceptor -> open transaction
3. CacheInterceptor -> check cache
4. Target method -> your code
5. Commit/Rollback
6. Return result
-> Each interceptor = one Advice
-> Order determined by @Order
How to Check for a Proxy
// In debugger:
// JDK: com.sun.proxy.$Proxy123
// CGLIB: UserService$$EnhancerBySpringCGLIB$$abc123
// Programmatically:
AopUtils.isAopProxy(bean);
AopUtils.isCglibProxy(bean);
AopUtils.getTargetClass(proxy); // when you work with an object but don't know if it's a proxy or original (e.g., when comparing types via instanceof or getDeclaredMethod).
Production Experience
Real scenario: final method not proxied
@Service
public class UserService {
@Transactional
public final void save(User user) { } // final!
}
// CGLIB creates a subclass and overrides methods.
// final methods cannot be overridden, so the call goes
// directly - around proxy logic (transactions, logging, etc.)
// Transaction is NOT created!
// -> Data saved WITHOUT transaction
// -> Bug in production!
Best Practices
- JDK Proxy -> via interfaces
- CGLIB -> via inheritance (not final!)
- Self-invocation -> transaction does not work
- AopUtils.getTargetClass() -> for reflection
- Avoid final on proxied methods
- Spring Boot -> CGLIB by default
Summary for Senior
- Proxy = wrapper for AOP
- JDK = interfaces, CGLIB = inheritance
- Spring Boot -> CGLIB always
- Self-invocation = call around proxy
- final methods -> not proxied by CGLIB
- Interceptor chain -> order via @Order
Interview Cheat Sheet
Must know:
- Proxy = wrapper object that adds AOP functionality without changing code
- Two types: JDK Dynamic Proxy (via interfaces) and CGLIB (via inheritance)
- Spring Boot 2.x/3.x: proxy-target-class=true by default -> always CGLIB
- @Transactional, @Async, @Cacheable - all work through proxies
- Self-invocation: calling
this.method()goes directly, bypassing proxy - Final classes and final methods CANNOT be proxied via CGLIB
- Interceptor chain: Security -> Transaction -> Cache -> Target method
Common follow-up questions:
- How to check if a bean is a proxy? -> AopUtils.isAopProxy(bean), AopUtils.isCglibProxy(bean)
- Why doesn’t self-invocation work? -> this refers to Target, not Proxy
- How to get the original class from a proxy? -> AopUtils.getTargetClass(proxy)
- What is the difference between JDK Proxy and CGLIB? -> JDK requires interface, CGLIB creates a subclass
Red flags (DO NOT say):
- “CGLIB can proxy final methods” - cannot, inheritance is impossible
- “Self-invocation works with CGLIB” - does not work, this = Target
- “Proxy adds significant overhead” - nanoseconds, negligible
- “Spring creates a proxy for every bean” - only with AOP annotations
Related topics:
- [[14. When does Spring create a proxy]]
- [[15. What is AOP Aspect-Oriented Programming]]
- [[18. Why Transactional does not work with self-invocation]]
- [[19. How to solve the self-invocation problem]]