Question 12 · Section 5

Difference between Singleton and Prototype Scope?

4. Prototype -> clean resources yourself 5. Avoid cycles in Prototype 6. If you just need an object without DI, @PostConstruct and proxying - a regular new is faster than Protot...

Language versions: English Russian Ukrainian

Junior Level

  Singleton Prototype
Instances One New each time
@PreDestroy Called NOT called
When created On startup On request
@Service  // Singleton
public class A { }

@Scope("prototype")
@Service
public class B { }

A a1 = context.getBean(A.class);
A a2 = context.getBean(A.class);
a1 == a2  // -> true (same object!)

B b1 = context.getBean(B.class);
B b2 = context.getBean(B.class);
b1 == b2  // -> false (different objects!)

Middle Level

Singleton

// By default!
// Stored in singletonObjects cache
// -> Fast access, not created each time

// Must be Stateless!
// -> All threads use the same object
// -> Do not store data in fields!

Prototype

// Each request -> new object
// Full lifecycle: constructor -> @PostConstruct
// BUT: @PreDestroy is NOT called!

// You are responsible for cleanup:
PrototypeBean bean = context.getBean(PrototypeBean.class);
try {
    // usage
} finally {
    bean.cleanup();  // Manually!
}

Prototype in Singleton Problem

// Prototype "freezes"
@Service
public class SingletonService {
    @Autowired PrototypeBean proto;  // Created ONCE!
}

// Why: Singleton is created once, and all its dependencies are injected
// at creation time. PrototypeBean is created once during injection and
// then reused - Spring does not create a new instance on each
// access to the singleton bean.

// ObjectProvider
@Service
public class SingletonService {
    @Autowired ObjectProvider<PrototypeBean> provider;

    public void process() {
        PrototypeBean proto = provider.getObject();  // New!
    }
}

Senior Level

Circular Dependencies

Singleton:
  -> Spring can resolve via earlySingletonObjects

Prototype:
  -> Unresolvable in principle!
  -> A -> B -> A -> infinite recursion
  -> BeanCurrentlyInCreationException

Performance Overhead

Singleton:
  -> Created once
  -> Fast access from cache

Prototype:
  -> Full creation cycle each time
  -> Reflection, BPP, @PostConstruct
  -> On frequent requests -> slow!

Memory Leaks

Prototype with resources:
  -> Opened file/connection
  -> Spring does not call @PreDestroy
  -> Resource not released -> leak!

Solution:
  -> try-finally with cleanup()
  -> Or BeanPostProcessor for tracking

Production Experience

Real scenario: Prototype leak

In a scenario where each Prototype bean opens a connection:
10,000 requests/sec -> 10,000 connections -> DB crashes

Solution: Singleton + ObjectPool

Best Practices

  1. Singleton - 95% of cases (Stateless!)
  2. Prototype - heavy stateful objects
  3. ObjectProvider - Prototype in Singleton
  4. Prototype -> clean resources yourself
  5. Avoid cycles in Prototype
  6. If you just need an object without DI, @PostConstruct and proxying - a regular new is faster than Prototype from Spring context.

Summary for Senior

  • Singleton = cache, Stateless, @PreDestroy works
  • Prototype = new each time, @PreDestroy does NOT work
  • Prototype in Singleton -> ObjectProvider
  • Cycles -> unresolvable for Prototype
  • Resources -> clean manually in Prototype
  • Performance -> Prototype is more expensive than Singleton

Interview Cheat Sheet

Must know:

  • Singleton = one instance, cached; Prototype = new on each getBean()
  • @PreDestroy is only called for Singleton, for Prototype - NEVER
  • Prototype in Singleton = problem: Prototype is created once and “freezes”
  • Circular dependencies are resolvable for Singleton (via earlySingletonObjects), but NOT for Prototype
  • Performance: Prototype is more expensive - full creation cycle each time (reflection, BPP, @PostConstruct)
  • Resources in Prototype - leak, clean manually via try-finally
  • If you just need an object without DI - use new, not Prototype

Common follow-up questions:

  • How to get a new Prototype inside a Singleton? -> ObjectProvider.getObject()
  • Why are circular dependencies unresolvable for Prototype? -> Infinite creation recursion, Spring throws BeanCurrentlyInCreationException
  • Where is Singleton stored? -> singletonObjects cache (ConcurrentHashMap)
  • When is Prototype justified? -> Heavy stateful objects, when Singleton + ThreadLocal cannot be used

Red flags (DO NOT say):

  • “@PreDestroy works for Prototype” - does not work
  • “Singleton is created on each request” - created once
  • “Prototype is faster than Singleton” - opposite, full cycle each time
  • “Cycles in Prototype are resolved via @Lazy” - unresolvable in principle

Related topics:

  • [[11. What scopes exist in Spring]]
  • [[13. What is a proxy in Spring]]
  • [[19. How to solve the self-invocation problem]]