spring为什么需要三级缓存解决循环依赖
三级缓分别存缓存了什么
直接说结论
1.【三级】缓存singletonFactories缓存的是已经实例化,但还未进行属性注入的bean。也就是只执行了createBeanInstance后产生的bean。
2.【二级】缓存earlySingletonObjects缓存的是已经实例化,但还未进行属性注入,但是已经在执行populateBean过程中进行依赖解析时,被其他的bean当作属性注入的bean。也就是只执行了createBeanInstance,在populateBean执行过程中还未被属性注入的bean。
3.【一级】缓存singletonObjects缓存的是已经实例化,并属性注入,并初始化成功了的对象。也就是执行了createBeanInstance -> populateBean -> initializeBean [成功]之后的bean。 在spring完全启动成功后,所有的单例对象都应该在这里面。
三级和二级的区别是,三级中存储的bean是刚产生,还未被任何其他的bean所依赖(也就是没被注入到其他bean的属性中)。二级中存储的bean是刚产生,但因为循环依赖还未进行属性注入之前,在进行属性依赖的解析时,被传入自己的依赖中了(因为循环依赖),这时候这个bean将被从三级缓存中移除,加入二级缓存中。
简单来说,一个bean在这三个缓存中转移的时机是这样的:首先createBeanInstance产生一个bean, 将这个bean加入三级缓存,然后进行populateBean如果这个bean和另一个bean产生了循环依赖被注入到另一个bean的属性中,将这个bean从三级缓存移动到二级缓存,如果暂时还没有被其他bean依赖,将还在三级缓存中,populateBean完成后,进行initializeBean, initalizeBean完成后将这个bean从二级或三级中移动到一级缓存中。
// 加入三级缓存的方法
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized(this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
// 三级缓存转移到二级缓存的方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
// 二三级缓存转移到一级缓存的方法
protected void addSingleton(String beanName, Object singletonObject) {
synchronized(this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
为什么需要三级缓存
为什么需要两级缓存,我们就不讨论了。我们来讨论为什么需要第三层。我们知道三级缓存和二级缓存中的对象其实区别就是是否已经被其他bean所依赖(是否已经被注入到其他bean的属性中)。我们知道在InitializeBean当中主要有这个几个步骤。1.执行Aware方法。 2.执行BeanPostProcessor的beforeInstantiation方法 3.执行初始化的几种方法。4.执行BeanPostProcessor的afterInstatiation方法。其中我们看一下BeanPostProcessor的两个方法。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return null;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return null;
}
}
这两个方法传入一个bean,然后返回一个bean。但是并不要求是同一个bean。也就是经过这个BeanPostProcessor后我完全可以将,我们生成的bean用另一个bean替换掉然后加入到容器中。然而事实是这样的吗,并不是这样。因为可能会出现这么一个问题,如果我们原本生成的bean已经被其他bean所依赖已经注入到了其他bean的属性中(其实这种情况只可能发生在循环依赖的populateBean的时候), 我们在这里用新的bean将原本的bean替换替换掉放入容器中(即一级缓存),那么后面再依赖这个beanName的bean注入的将是这个新的bean,也就是在代码中autowired注入的同一个beanName的不同bean里会出现两个不同的bean,这个就出现了不一致,不是单例了。所以在InitializeBean以后我们必须判断,进行BeanPostProcessor的处理前后还是不是同一个bean。如果是同一个bean那没一点问题,直接从二级或三级缓存移动到一级缓存中。如果不是同一个bean那就报错…吗?当然不是,如果不是同一个bean就报错,那BeanPostProcessor就不应该设计有返回值让我们可以替换犯错的可能。毕竟如果只是为了让我们能对bean的属性进行射值,传入一个引用对象不需要返回值就可以做到。那也就是说如果处理前后就算不是同一个bean也是可以的,但是是有条件的,什么条件呢,就是如果被替换的对象目前还没有被其他的bean引用。那样的话,就算我们在这里用新的bean替换掉原本的bean, 后面实例化的其他bean也只会引用这个新的bean,在整个环境中不会出现具有相同beanName的两个不同的bean!我们怎么判断一个bean当前有没有被其他bean引用,就看三级缓存中有没有这个beanName的bean就行了。如果三级缓存中有,此时也是允许替换的。
// 这个bean是原本的bean
Object exposedObject = bean;
try {
this.populateBean(beanName, mbd, instanceWrapper);
// eposedObject是被处理后的bean
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
//....
}
// 这个地方是必进的
if (earlySingletonExposure) {
// 注意getSingleton第二个参数false, 指不从三级缓存,只从一二级缓存中拿
Object earlySingletonReference = this.getSingleton(beanName, false);
// ealySingeltonReference为null的话说明,bean在三级缓存中,不在一二级中,此时bean没有被其他bean引用
// 不需要判断bean是否被替换 如果不为null,说明已经被其他bean引用,需要判断是否被替换
if (earlySingletonReference != null) {
// 还是同一个对象,没得问题
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
// 如果不是同一个对象 会抛出异常
// 注意异常中得信息 This means that said other beans do not use the final version of the bean.
// 这就是说其他的bean(指之前就已经依赖这个bean的bean)可能没有使用最终版的bean.(指新的bean) 他们使用的是之前的bean.
String[] dependentBeans = this.getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
String[] var12 = dependentBeans;
int var13 = dependentBeans.length;
for(int var14 = 0; var14 < var13; ++var14) {
String dependentBean = var12[var14];
if (!this.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.");
}
}
}
}
为什么允许BeanPostProcessor替换bean, 其实主要是为了实现动态代理,在这里可以替换为代理对象。但是不是BeanPostProcessor来实现,而是其子类InstantiationAwareBeanPostProcessor。因为BeanPostProcessor如果要实现代理类的替换,由我们上面的讨论可知,必须保证原来的bean此时还没有被其他bean所依赖,而bean得加载顺序不是我们能控制的,所以在这里替换根本行不通。InstantiationAwareBeanPostProcessor则在createBean方法之前就进行调用,将生成的代理类直接返回放入容器,根本不会进行后面的createBeanInstance,populateBean等操作。容器中只会存在代理类。 由此看来其实spring中还是有很多的硬编码的。都是BeanPostProcessor却有不同的调用时机,还不如用两个接口来表示不容易导致使用者搞混。
Object beanInstance;
try {
// 调用InstantiationAwareBeanPostProcessor产生代理对象
beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);
if (beanInstance != null) {
// 有代理对象直接返回
return beanInstance;
}
} catch (Throwable var10) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", var10);
}
try {
// doCreateBean还没执行
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
} catch (ImplicitlyAppearedSingletonException | BeanCreationException var7) {
throw var7;
} catch (Throwable var8) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", var8);
}
// 调用InstantiationAwareBeanPostProcessor
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// 有InstantiationAwareBeanPostProcessor的话
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = this.determineTargetType(beanName, mbd);
if (targetType != null) {
bean = this.applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = this.applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = bean != null;
}
return bean;
}