Spring使用三级缓存解决循环依赖?终于完全弄明白了

文章阅读前推荐

推荐先去看看源码,源码很短,但是对于我们在脑子里构建一个完整思路很重要。看起来非常简单,只需要双击shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到550行左右的doCreateBean方法,重点看一下580行到600行这20行代码就行,包含了三级缓存、属性注入、初始化,精华都在这20行,实在没条件的可以直接看文末附带的doCreateBean方法源码

Spring可以自动解决的循环依赖

public class AService {
	@Autowired
    private BService bService;
}

public class BService {
	@Autowired
    private AService aService;
}

Spring无法自动解决构造器的循环依赖

public class DService {
    public DService(CService cService) {
    	...
    }
}

public class CService {
    public CService(DService dService) {
    	...
    }
}

源码中关于三级缓存的定义

// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

所以说,其实三级缓存就是三个Map而已

三级缓存有什么区别?

一级缓存

一级缓存里存的是成品对象,实例化和初始化都完成了,我们的应用中使用的对象就是一级缓存中的

二级缓存

二级缓存中存的是半成品,没有完成属性注入和初始化,用来解决对象创建过程中的循环依赖问题
早期暴露出去的Bean,其实也就是解决循环依赖的Bean。早期的意思就是没有完完全全创建好,但是由于有循环依赖,就需要把这种Bean提前暴露出去。其实 早期暴露出去的Bean 跟 完完全全创建好的Bean 他们是同一个对象,只不过早期Bean里面的注解可能还没处理,完完全全的Bean已经处理了完了,但是他们指的还是同一个对象,只不过它们是在Bean创建过程中处于的不同状态

三级缓存

三级缓存中存的是 ObjectFactory 类型的代理工厂对象,用于处理存在 AOP 时的循环依赖问题
存的是每个Bean对应的ObjectFactory对象,通过调用这个对象的getObject方法,就可以获取到早期暴露出去的Bean。
注意:这里有个很重要的细节就是三级缓存只会对单例的Bean生效,像多例的是无法利用到三级缓存的,通过三级缓存所在的类名DefaultSingletonBeanRegistry就可以看出,仅仅是对SingletonBean也就是单例Bean有效果。

发生循环依赖时的执行流程(精华必读)

正常不存在循环依赖的A、B对象是依次创建的,但是如果存在循环依赖的话,创建A的过程中,会顺便把B也创建了。注意,每次获取bean对象都会先去一级缓存看有没有值。
具体流程是:
1、遍历待创建的所有beanName,第一次遍历,开始获取A,此时缓存中没有,会开始正常创建流程
2、A初始创建完成,然后判断A是否是单例,且没有创建完毕,如果是,那么就会把A的beanFactory存入三级缓存
3、A开始处理@Autowired注解,开始注入B属性,于是尝试从缓存获取B,获取不到,则开始正常创建B的流程
4、B初始创建完成,同样判断B是否是单例,且没有创建完毕,如果是,那么就会把B的beanFactory存入三级缓存
5、B开始处理@Autowired注解,开始注入A属性,于是依次从一级缓存、二级缓存查找A属性,都没有就尝试从三级缓存获取A的beanFactory,通过beanFactory.getObject()方法获取A属性,接下来把A存入二级缓存,清除三级缓存。因为此时能获取到A,所以B的A属性能填充成功,B接着执行初始化,B处于实例化、初始化都完成的完全状态
6、B执行addSington(),把完全状态的B存入一级缓存,清空二三级缓存(实际只有三级有值)
7、A继续开始填充B属性,于是调用beanFactory.getBean()获取B,第六步已经把B存入一级缓存,此时直接返回,填充成功,继续执行初始化,得到一个完全状态的A
8、A执行addSington(),把完全状态的A存入一级缓存,清空二三级缓存(实际只有二级有值)
9、第二次遍历,开始获取B,此时一级缓存中有B,直接返回。
至此A、B全部实例化、初始化完成

疑惑解答

问题一:步骤5中,为什么要把B放入二级缓存?
答:主要是怕还有其他的循环依赖,如果还有的话,直接从二级缓存中就能拿到早期的AService对象

问题二:步骤6中,为什么要清空二三级缓存?
答:因为后续其他bean中也需要注入B时,会按顺序从一级缓存直到三级缓存查找,一级缓存有了,二三级缓存中的就不需要了,节省空间

问题三:文章开头提到,构造器造成的循环依赖三级缓存解决不了,为什么?
答:因为构造器循环依赖是发生在bean实例化阶段,此时连早期对象都还没创建出来,拿什么放到三级缓存。三级缓存只能是在bean实例化之后,才能起到作用

问题四:不用三级缓存,只用二级缓存能不能解决循环依赖?
答:不能,因为通过ObjectFactory获取的Bean可能是两种类型,第一种就是实例化阶段创建出来的对象,还是一种就是实例化阶段创建出来的对象的代理对象。至于是不是代理对象,取决于你的配置,如果添加了事务注解又或是自定义AOP切面,那就需要代理。假设舍弃第三级缓存,也就是没有ObjectFactory,那么就需要往第二缓存放入早期的Bean,那么是放没有代理的Bean还是被代理的Bean呢,这是在后面的属性注入阶段,处理注解的时候才能分辨的?
1)如果直接往二级缓存添加没有被代理的Bean,那么可能注入给其它对象的Bean跟最后最后完全生成的Bean是不一样的,因为最后生成的是代理对象,这肯定是不允许的;
2)那么如果直接往二级缓存添加一个代理Bean呢?
● 假设没有循环依赖,提前暴露了代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错,
● 假设没有循环依赖,使用了ObjectFactory,那么就不会提前暴露了代理对象,到最后生成的对象是什么就是什么,就不会报错,
● 如果有循环依赖,不论怎样都会提前暴露代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错
通过上面分析,如果没有循环依赖,使用ObjectFactory,就减少了提前暴露代理对象的可能性,从而减少报错的可能。

问题五:如果把二级缓存去掉,只留下一级、三级缓存呢?
答:假设舍弃第二级缓存,也就是没有存放早期的Bean的缓存,其实肯定也不行。上面说过,ObjectFactory其实获取的对象可能是代理的对象,那么如果每次都通过ObjectFactory获取代理对象,那么每次都重新创建一个代理对象,这肯定也是不允许的。

doCreateBean()方法源码(带注释)

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		//1、通过BeanDefinition实例化对象
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//对象是否单例、是否未创建完成
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//将对象的工厂加入到三级缓存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//属性注入(在这里解析@Autowired注解时,触发循环依赖)
			populateBean(beanName, mbd, instanceWrapper);
			//初始化
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		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 " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}



从缓存中获取Bean的源码

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存中获取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 如果一级缓存里没有 且 bean正在创建中
        synchronized (this.singletonObjects) {
            // 从二级缓存里获取
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 二级缓存没有 从三级缓存获取一个工厂
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 能获取到工厂 则创建bean
                    singletonObject = singletonFactory.getObject();
                    // 把实例存入二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 把工厂从三级缓存移除
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}




–我是“三七有脾气”,一个在互联网"苟且偷生"的Java程序员
“如果感觉博客对你有用,麻烦给个点赞、评论、收藏,谢谢

你可能感兴趣的:(spring,spring,缓存,java)