1. Circular dependency scenario
Assume that there is an interdependence between two beans:
@Component public class ServiceA { @Autowired private ServiceB serviceB; } @Component public class ServiceB { @Autowired private ServiceA serviceA; }
Level 2 and Level 3 cache definition
existDefaultSingletonBeanRegistry
Definition in:
// Level 1 cache: complete bean (K:Bean name V: instantiation + initialization bean)private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // Level 2 cache: Early exposure object (K:Bean name V: Original bean with unfinished attribute injection)private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // Level 3 cache: Object factory (K:Bean name V:ObjectFactory)private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
3. Solution process (taking ServiceA and ServiceB as examples)
1. Create a Service
sequenceDiagram participant Container participant Cache1 as singletonObjects participant Cache2 as earlySingletonObjects participant Cache3 as singletonFactories Container->>Cache1: examineServiceADoes it exist Cache1-->>Container: Does not exist Container->>Container: InstantiationServiceA(Constructor call) Container->>Cache3: Add toServiceAofObjectFactory Container->>Container: Start attribute injection(needServiceB)
Key code segments:
// AbstractAutowireCapableBeanFactory#doCreateBean addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
2. Found that ServiceB is required
sequenceDiagram participant Container participant Cache1 participant Cache2 participant Cache3 Container->>Cache1: examineServiceBDoes it exist Cache1-->>Container: Does not exist Container->>Container: InstantiationServiceB(Constructor call) Container->>Cache3: Add toServiceBofObjectFactory Container->>Container: Start attribute injection(needServiceA)
3. Solve ServiceB's dependence on ServiceA
sequenceDiagram participant Container participant Cache1 participant Cache2 participant Cache3 Container->>Cache1: FindServiceA Cache1-->>Container: Does not exist Container->>Cache2: FindServiceA Cache2-->>Container: Does not exist Container->>Cache3: GetServiceAofObjectFactory Container->>Container: implementgetEarlyBeanReference() Container->>Cache2: Will生成of代理对象存入earlySingletonObjects Container->>ServiceB: injectionServiceAof早期引用 Container->>Container: FinishServiceBinitialization Container->>Cache1: WillServiceBPut insingletonObjects
4. Backtracking complete ServiceA initialization
sequenceDiagram participant Container participant Cache1 participant Cache2 participant Cache3 Container->>ServiceA: Inject initializedServiceB Container->>Container: implementServiceAThe initialization method Container->>Cache1: WillServiceAPut insingletonObjects Container->>Cache2: RemoveServiceAEarly citations Container->>Cache3: RemoveServiceAofObjectFactory
4. Detailed explanation of key mechanisms
1. The core role of getEarlyBeanReference()
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { // Generate proxy objects here if necessary if (!() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; bean = (exposedObject, beanName); } } } return bean; }
2. Analysis of the necessity of level 3 cache
Cache Level | Problems solved | Typical scenarios |
---|---|---|
singletonFactories | Handle delayed generation of AOP proxy | A singleton of proxy objects needs to be guaranteed |
earlySingletonObjects | Avoid duplicate creation of early references | Multiple Beans depend on the same bean that has not been initialized |
singletonObjects | Storage of the ultimate bean available | Normal bean acquisition |
V. Design constraints and limitations
1. Only singleton scopes are supported
Prototype scoped beans cannot solve circular dependencies because Spring does not cache prototype beans
2. Constructor injection limit
If the circular dependency occurs through the constructor injection, it cannot be resolved (dependency injection needs to be completed before instantiation)
3. Risk of asynchronous initialization
use@Async
Beans enhanced by methods such as the may destroy the initialization order
6. Debugging skills
Check cache status
existDefaultSingletonBeanRegistry
Set breakpoints in the class:
// View Level 3 cache content("singletonFactories: " + ()); ("earlySingletonObjects: " + ()); ("singletonObjects: " + ());
Forced throwing a circular dependency exception
Add in the configuration class:
@Bean public CircularReferencesBean circularReferencesBean() { return new CircularReferencesBean(circularReferencesBean()); }
7. Performance optimization suggestions
Avoid overuse of circular dependencies
Even if the technology is feasible, it should be decoupled through design patterns (such as event-driven)
Use @Lazy annotation reasonably
Lazy loading non-essential dependencies:
@Autowired @Lazy private ServiceB serviceB;
Monitor cache hit rate
Monitoring via JMXsingletonObjects
andearlySingletonObjects
The ratio of
Summarize
Spring's Level 3 caching mechanism passesExpose object references in advance + Dynamic proxy generationThe collaborative design of the system elegantly solves the circular dependency problem while ensuring singletonity. Understanding this mechanism requires focusing on the stage division of the Bean life cycle and the transformation logic of cache state.
The above is personal experience. I hope you can give you a reference and I hope you can support me more.