Backend Development 10 min read

Why Spring Requires a Three‑Level Cache to Resolve Circular Dependencies Instead of a Two‑Level Cache

This article explains how Spring's bean lifecycle uses a three‑level cache (singletonObjects, earlySingletonObjects, singletonFactories) to break circular dependencies, especially when AOP proxies are involved, and why a two‑level cache alone cannot guarantee correct singleton behavior.

Architect's Guide
Architect's Guide
Architect's Guide
Why Spring Requires a Three‑Level Cache to Resolve Circular Dependencies Instead of a Two‑Level Cache

In everyday Spring development, circular dependencies between beans occur frequently, and Spring automatically resolves them for developers. This article analyzes how Spring solves bean circular dependencies and why it employs a three‑level cache rather than a two‑level cache.

Bean Lifecycle

Understanding the bean lifecycle in Spring is essential to grasp how circular dependencies are handled. The AbstractAutowireCapableBeanFactory.doCreateBean method starts bean creation by instantiating the bean (using reflection) without injecting @Autowired properties yet.

Next, populateBean fills bean properties; if a required dependency is not yet created, Spring recursively creates that dependency. After property injection, initializeBean invokes aware interfaces, BeanPostProcessor methods, and any custom init methods.

During initialization, Spring calls aware methods (e.g., setBeanName , setBeanClassLoader , setBeanFactory ) and executes postProcessBeforeInitialization and postProcessAfterInitialization for all BeanPostProcessor implementations. Notably, a bean that itself implements BeanPostProcessor will not have its own postProcessBeforeInitialization invoked until it is fully loaded into the singleton cache.

Three‑Level Cache for Circular Dependency Resolution

When populating properties, if Spring discovers a dependency that has not been created, it creates the dependent bean. Spring exposes the partially created bean via an ObjectFactory placed in the third‑level cache ( singletonFactories ), which holds a factory for an early bean reference.

The three caches are:

singletonObjects – first‑level cache for fully initialized singletons.

earlySingletonObjects – second‑level cache for early references (partially initialized beans).

singletonFactories – third‑level cache storing factories that can produce early references.

Scenario: AService depends on BService, and BService depends on AService.

AService is instantiated and its ObjectFactory is stored in the third‑level cache.

When injecting BService, Spring sees BService is missing and creates it, also storing its factory in the third‑level cache.

While injecting AService into BService, Spring retrieves the early reference from the third‑level cache, calls getObject() , which may return a proxy if AOP is applied.

The early reference is moved to the second‑level cache ( earlySingletonObjects ) to avoid creating multiple proxy instances.

If only two caches were used, each call to singletonFactory.getObject() could produce a new proxy for an AOP‑enhanced bean, breaking the singleton contract. The second‑level cache ensures the same proxy instance is reused, making the three‑level approach necessary when AOP is involved.

Summary

The bean loading process, combined with the three‑level cache, enables Spring to resolve circular dependencies safely, even when beans are wrapped by AOP proxies. Removing the second‑level cache would work only for non‑proxied beans, but would fail to maintain singleton integrity for proxied beans.

In practice, experimenting with AOP scenarios demonstrates why the three‑level cache is essential.

aopBackend DevelopmentSpringCircular DependencyThree-level Cachebean lifecycle
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.