spring源码-之解决循环依赖
Spring如何解决循环依赖
为了解决循环依赖,Spring 使用了三级缓存。一级缓存用于存储 bean 定义。二级缓存用于存放已经创建但还没有完全初始化的前期bean实例。三级缓存用于存放完全初始化的bean实例。
当检测到循环依赖时,Spring 会创建一个部分初始化的 bean 实例并将其存储在二级缓存中。这允许 Spring 通过将部分初始化的 bean 实例注入到依赖它的另一个 bean 中来解决循环依赖。一旦解决了循环依赖,Spring 就可以完全初始化 bean 实例并将其存储在三级缓存中。
核心源码来自 Spring 框架中的 AbstractAutowireCapableBeanFactory 类。此类负责创建和初始化 bean 实例。核心部分在doCreateBean 方法种,该方法在创建新 bean 实例时调用。
以下为部分代码
/d:/study/spring-framework/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
if (instanceWrapper == null) { // Check if instanceWrapper is null
instanceWrapper = createBeanInstance(beanName, mbd, args); // instanceWrapper if it is null
}
Object bean = instanceWrapper.getWrappedInstance(); // Get the wrapped instance from the instanceWrapper
Class<?> beanType = instanceWrapper.getWrappedClass(); // Get the wrapped class from the instanceWrapper
if (beanType != NullBean.class) { // Check if the beanType is not NullBean
mbd.resolvedTargetType = beanType; // Set the resolvedTargetType to the beanType
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) { // Synchronize on the postProcessingLock
if (!mbd.postProcessed) { // Check if the bean definition has not been post-processed
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); // Apply post-processors to the merged bean definition
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex); // Throw a BeanCreationException if post-processing fails
}
mbd.postProcessed = true; // Set the postProcessed flag to true
}
}
// 提前暴露单例对象解决循环依赖(循环依赖)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName)); // Check if early singleton exposure is allowed
if (earlySingletonExposure) { // 如果是提前暴露了
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references"); // Log a message indicating that the bean is being eagerly cached
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); // 添加bean进入三级缓存
}
// 初始化实例
Object exposedObject = bean; // Set the exposedObject to the bean
try {
populateBean(beanName, mbd, instanceWrapper); // Populate the bean with property values
exposedObject = initializeBean(beanName, exposedObject, mbd); // Initialize the bean
}
catch (Throwable ex) { // Catch any exceptions that occur during initialization
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex; // If the exception is a BeanCreationException and the bean name matches, re-throw the exception
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); // Otherwise, throw a new BeanCreationException
}
}
// 如果提前暴露对象,则尝试从缓存中获取。
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}