When Does Spring Create a Proxy?
4. Do not proxy final classes/methods 5. Minimize number of proxies 6. AopUtils -> for proxy check
Junior Level
Spring creates a proxy when it sees special annotations:
| Annotation | What proxy adds |
|---|---|
| @Transactional | Transaction management |
| @Async | Asynchronous execution |
| @Cacheable | Result caching |
| @PreAuthorize | Access right check |
@Service
public class MyService {
@Transactional // Proxy for transactions
public void save() { }
@Async // Proxy for asynchrony
public CompletableFuture<Void> process() { }
}
Middle Level
Functional Triggers
@Transactional:
Proxy -> opens transaction -> calls method -> commit/rollback
@Async:
Proxy -> passes task to TaskExecutor -> returns Future immediately
@Cacheable:
Proxy -> checks cache -> if exists -> returns from cache
-> if not -> calls method -> saves to cache
Scoped Proxies:
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
// Proxy looks for actual bean in RequestContextHolder
Creation Mechanism
BeanPostProcessor.postProcessAfterInitialization():
-> AnnotationAwareAspectJAutoProxyCreator scans bean
-> Found annotation -> creates proxy
-> Proxy replaces original bean in context
Senior Level
AnnotationAwareAspectJAutoProxyCreator
Main BPP for proxy creation:
1. Scans all annotations and @Aspect
2. For each bean checks Pointcut
3. If matches -> creates proxy
4. Proxy goes further down the BPP chain
Avoiding Unnecessary Proxies
Proxy = overhead (memory + CPU)
-> Do not put @Transactional on methods that only read and do not need a consistent data snapshot. If transaction is needed, use readOnly = true for Hibernate optimization (skip dirty checking).
-> Proxy is created for the ENTIRE bean, even if there is one annotation
Production Experience
Real scenario: unnecessary proxy
Each proxy - additional object in memory and small call overhead (on the order of nanoseconds). With many beans this adds up.
Solution: removed @Transactional from read methods
-> Fewer proxies, less overhead
Best Practices
- @Transactional -> only where needed
- @Async -> carefully, proxy on entire bean
- Scoped Proxy -> for web scope in Singleton
- Do not proxy final classes/methods
- Minimize number of proxies
- AopUtils -> for proxy check
Summary for Senior
- Proxy created for @Transactional, @Async, @Cacheable
- BPP -> postProcessAfterInitialization
- Scoped Proxy -> for web scope in Singleton
- Each proxy = memory + CPU overhead
- final -> not proxied
- AnnotationAwareAspectJAutoProxyCreator -> main creator
Interview Cheat Sheet
Must know:
- Proxy created for @Transactional, @Async, @Cacheable, @PreAuthorize
- Mechanism: AnnotationAwareAspectJAutoProxyCreator (BeanPostProcessor) in postProcessAfterInitialization()
- Scoped Proxy (@Scope with proxyMode) - creates proxy for web scope in Singleton beans
- Each proxy = small memory + CPU overhead (nanoseconds per call)
- Spring scans all annotations and @Aspect, checks Pointcut, creates proxy
- @Transactional on read methods without need is extra overhead - use readOnly = true
Common follow-up questions:
- Which component is responsible for proxy creation? -> AnnotationAwareAspectJAutoProxyCreator (BPP)
- When is a Scoped Proxy created? -> On
@Scope(value="request", proxyMode=TARGET_CLASS) - Can proxy creation be avoided? -> Yes, do not put annotations where not needed
- How does Spring decide which proxy type to create? -> proxy-target-class=true -> CGLIB, false + has interface -> JDK Proxy
Red flags (DO NOT say):
- “Proxy is created for every bean” - only with AOP annotations
- “@Transactional on read methods is always required” - extra overhead without need
- “Proxy adds noticeable delay” - nanoseconds
- “Final classes are proxied via CGLIB” - not proxied
Related topics:
- [[11. What scopes exist in Spring]]
- [[13. What is a proxy in Spring]]
- [[15. What is AOP Aspect-Oriented Programming]]
- [[17. What does the Transactional annotation do]]