为何spring单例要使用三级缓存

熟悉spring框架的同窗应该都知道spring单例使用的三级缓存,简单回顾下哪三级缓存,源码类:DefaultSingletonBeanRegistryjava

  1. 一级缓存:singletonObjects
  2. 二级缓存:earlySingletonObjects
  3. 三级缓存:singletonFactories

直接使用一级缓存不能够吗?

一级缓存,也就是直接将单例bean缓存至singletonObjects,去除其余缓存。设想下面这个场景web

  1. 建立单例A
  2. 初始化单例A(单例A强依赖单例B),注入单例B
  3. 从工厂中获取单例B发现不存在,则建立
  4. 建立单例B
  5. 初始化单例B(单例B强依赖单例A),注入单例A
  6. 从工厂中获取单例A发现不存在(由于正在建立中),则建立??????

没错,问题来了,循环依赖,死循环,直接凉凉。spring

直接使用二级缓存不能够吗?

对于直接使用一级缓存的问题已经暴露,如何解决?那么有同窗就要问了,那我在建立单例A时就直接将单例A放入缓存中不能够吗?这样不就解决了循环依赖问题?是的,这样处理是能够的。可是咱们如何判断从工厂中获取的一个单例初始化完成了?对单例的全部依赖注入的属性进行一次判空来判断?依赖注入的实例是否完成了初始化?使用工厂中的单例还要各类判空是否是很恶心?增长二级缓存岂不快哉
使用二级缓存:earlySingletonObjects+singletonObjectsexpress

  1. 建立单例A
  2. 单例A放入二级缓存:earlySingletonObjects
  3. 初始化单例A(单例A强依赖单例B),注入单例B
  4. 从工厂中获取单例B发现不存在,则建立
  5. 建立单例B
  6. 单例B放入二级缓存:earlySingletonObjects
  7. 初始化单例B(单例B强依赖单例A),注入单例A
  8. 从一级缓存singletonObjects获取单例A,存在则返回,不存在继续查询二级缓存:earlySingletonObjects,若是不存在则建立单例A(此时发现循环依赖抛出异常),可是看到前面已经缓存在二级缓存中
  9. 注入单例A(多是未完成初始化的单例A)完成
  10. 初始化单例B完成,移除二级缓存中的单例B,缓存单例B至一级缓存,返回单例B
  11. 注入单例B完成
  12. 初始化单例A完成,移除二级缓存中的单例A,缓存单例A至一级缓存,返回单例A

能够看到咱们以前的问题所有迎刃而解,只要从工厂中的一级缓存中获取的单例均是完成了初始化,而且依赖注入的实例也均是完成了初始化,只是若是是循环依赖场景,可能获得的依赖注入时未完成初始化的,即某些依赖注入属性可能为null,例如:@Value注入的一些值。缓存

为何spring单例要使用三级缓存?

那么二级缓存已经解决了问题,为何还要引入三级缓存呢?设想下,若是我想要在读取二级缓存:earlySingletonObjects对象时统一增长一些日志或者其余处理动做,如何解决?在读取earlySingletonObjects对象实例后,对实例进行回调某些接口方法?固然没问题,能够看到spring对于非单例bean也采用该方法来处理前置、后置动做的回调。框架

else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
	try {
		beforePrototypeCreation(beanName);
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
		afterPrototypeCreation(beanName);
	}
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// getNonSingletonFactoryBeanForTypeCheck方法中也有一样对prototype类型bean的先后置回调处理代码

可是每次修改回调动做都要对全部读取earlySingletonObjects对象的地方进行修改,岂不是很恶心?每次增长一个读取earlySingletonObjects对象的动做都要记得加入相应的回调。是否是很容器遗漏和出错?那么若是咱们缓存的是一个接口而不是一个单例对象问题是否是就解决了呢?只须要对接口增长对应的动做便可,例如:svg

  • 合并bean定义后回调:MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition
  • 自定义决定使用哪一个构造器构造bean:SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors
  • 建立bean前的属性值处理回调:InstantiationAwareBeanPostProcessor.postProcessPropertyValues
  • 建立bean前的处理回调:InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
  • 建立bean后的处理回调:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
  • 二级缓存bean的处理回调:SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference
  • 断言特殊bean的bean类型,例如FactoryBean:SmartInstantiationAwareBeanPostProcessor.predictBeanType

对于循环依赖注入的未完成初始化的单例,若是有特殊需求,指望保证其一些属性的提早注入,能够经过该扩展对提早曝光的对象进行一些个性化定制的处理,例如:将@Value的值提早从Environment中获取并注入到提早曝光的对象。而不须要修改框架代码,只须要增长对应的接口实现便可post

总结

  1. 循环依赖已经彻底解决了吗?并无,对于构造器注入的循环依赖时没法解决的,这个是无解的#-_-!!!
  2. 二级缓存是为了解决属性字段级别的循环依赖注入问题
  3. 三级缓存是为了加强框架的扩展性
  4. 对于非单例模式{@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)}的bean循环依赖问题依然存在,会抛出循环依赖异常
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a': Unsatisfied dependency expressed through field 'b'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?