Spring Circular Dependency Resolution with Three‑Level Cache and Early AOP Proxy
This article explains how Spring resolves circular dependencies by using a three‑level cache and early bean exposure, detailing the roles of singletonObjects, earlySingletonObjects, and singletonFactories, and showing how Spring AOP proxies are applied during the process.
Spring resolves circular dependencies by using a three‑level cache mechanism during bean creation.
The first‑level cache ( singletonObjects ) holds fully initialized beans; the second‑level cache ( earlySingletonObjects ) stores early‑exposed bean instances that have been instantiated but not yet populated; the third‑level cache ( singletonFactories ) stores ObjectFactory instances that can create early bean references, allowing post‑processing such as AOP proxying.
// AbstractBeanFactory.java
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);
...
}When a bean is being created, Spring registers a singleton factory that returns an early bean reference via getEarlyBeanReference . This method may invoke SmartInstantiationAwareBeanPostProcessor logic to apply early AOP proxying.
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Try first‑level cache
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// Try second‑level cache
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// Retrieve ObjectFactory from third‑level cache
ObjectFactory
singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// Obtain bean via getObject(), which applies getEarlyBeanReference
singletonObject = singletonFactory.getObject();
// Move bean to second‑level cache
this.earlySingletonObjects.put(beanName, singletonObject);
// Remove from third‑level cache
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}The early bean reference is wrapped with Spring AOP proxy if applicable, and stored in earlySingletonObjects . After property population and initialization, the bean may be replaced by the proxied instance from the second‑level cache.
AbstractAutoProxyCreator ensures that a bean already proxied early is not proxied again, using earlyProxyReferences to track original instances.
Thus, the combination of three‑level caching and early exposure allows Spring to break circular references while still applying AOP and other bean post‑processors.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.