Backend Development 13 min read

How Spring Resolves Circular Dependencies Using Early Exposure and Three‑Level Caches

The article explains Spring's three‑level cache mechanism—singletonObjects, earlySingletonObjects, and singletonFactories—and how early exposure combined with BeanPostProcessor hooks enables the framework to break circular dependencies while still applying AOP proxies before full bean initialization.

Top Architect
Top Architect
Top Architect
How Spring Resolves Circular Dependencies Using Early Exposure and Three‑Level Caches

The article dives into Spring's internal solution for circular bean dependencies, focusing on the early‑exposure strategy and the three‑level cache (singletonObjects, earlySingletonObjects, singletonFactories) that makes it possible.

Three‑level cache: singletonObjects stores fully created beans, earlySingletonObjects holds beans that have been instantiated but not yet populated, and singletonFactories keeps ObjectFactory instances that can produce an early reference when needed.

When doGetBean is invoked, Spring first tries to retrieve the bean from these caches:

protected
T doGetBean(final String name, @Nullable final Class
requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    final String beanName = transformedBeanName(name);
    Object bean;
    // 2. try to get bean from cache
    Object sharedInstance = getSingleton(beanName);
    ...
}

The getSingleton method checks the first‑level cache, then the second, and finally the third cache via an ObjectFactory :

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory
singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

The third cache exists so that a SmartInstantiationAwareBeanPostProcessor can intervene via getEarlyBeanReference , allowing Spring AOP (or other post‑processors) to create a proxy before the bean is fully initialized.

Adding the factory looks like this:

this.addSingletonFactory(beanName, () -> {
    return this.getEarlyBeanReference(beanName, mbd, bean);
});

The getEarlyBeanReference implementation in AbstractAutoProxyCreator caches the bean as an early proxy and wraps it if necessary:

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

During the normal post‑processing phase, Spring checks earlyProxyReferences ; if the bean was already proxied early, it skips creating another proxy, ensuring the final bean returned to the user is the AOP‑enhanced instance.

Understanding these steps clarifies why Spring does not simply place the raw instance into the second cache, how early exposure works with AOP, and how the three‑level cache guarantees both circular‑dependency resolution and proper proxying.

javaAOPSpringCircular DependencyBeanPostProcessorThree-level Cache
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.