Question 14 · Section 5

When Does Spring Create a Proxy?

4. Do not proxy final classes/methods 5. Minimize number of proxies 6. AopUtils -> for proxy check

Language versions: English Russian Ukrainian

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

  1. @Transactional -> only where needed
  2. @Async -> carefully, proxy on entire bean
  3. Scoped Proxy -> for web scope in Singleton
  4. Do not proxy final classes/methods
  5. Minimize number of proxies
  6. 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]]