一、什么事spring的三级缓存
典型回答
在Spring的BeanFactory体系中,BeanFactory是Spring IOC容器的基础接口,其DefaultSingletonBeanRegistry类实现了BeanFactory接口,并维护了三级缓存:
public class DefaultSingletonBeanRegistyr extends SimpleAliasRegistry implements SingletonBeanRegistry {
//一级缓存,保存完成的Bean对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//三级缓存,保存单例Bean的创建工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//二级缓存,存储"半成品"的Bean对象
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
}
**singletonObjects是一级缓存,存储的是完整创建好的单例bean对象。**在创建一个单例bean时,会先从singletonObjects中尝试获取该bean的实例,如果能够获取到,则直接返回该实例,否则继续创建该bean。
**earlySingletonObjects是二级缓存,存储的是尚未完全创建好的单例bean对象。**在创建单例bean时,如果发现该bean存在循环依赖,则会先创建该bean的"半成品"对象,并将"半成品"对象存储到earlySingletonObjects中。当循环依赖的bean创建完成后,Spring会将完整的bean实例对象存储到singletonObjects中,并将earlySingletonObjects中存储的代理对象替换为完整的bean实例对象。这样可以保证单例bean的创建过程不会出现循环依赖问题。
**singletonFactories是三级缓存,存储的是单例bean的创建工厂。**当一个单例bean被创建时,Spring会先将该bean的创建工厂存储到singletonFactories中,然后再执行创建工厂的getObject()方法,生成该bean的实例对象。在该bean被其他bean引用时,Spring会从singletonFactories中获取该bean的创建工厂,创建出该bean的实例对象,并将该bean的实例对象存储到singletonObjects中。
二、Spring解决循环依赖一定需要三级缓存吗
典型回答
使用二级缓存也能解决循环依赖问题,但是如果完全依赖二级缓存解决循环依赖,意味着当我们依赖了一个代理类的时候,就需要在Bean实例化之后完成AOP代理,而在Spring的设计中,为了了解Bean的初始化和代理,是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理的。
但是,在Spring的初始化过程中,他是不知道哪些Bean可能有循环依赖的,那么,这时候Spring面临两个选择:
- 不管有没有循环依赖,都提前把代理对象创建出来,并将代理对象缓存起来,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
- 不提前创建代理对象,在出现循环依赖时,再生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。
第一个方案看上去比较简单,只需要二级缓存就可以了。但是他也意味着,Spring需要在所有的bean的创建过程中就要先成代理对象再初始化;那么这就和spring的aop的设计原则(前文提到的:在Spring的设计中,为了解耦Bean的初始化和代理,是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理的)是相悖的。
而Spring为了不破坏AOP的代理设计原则,则引入第三级缓存,在三级缓存中保存对象工厂,因为通过对象工厂我们可以在想要创建对象的时候直接获取对象。有了它,在后续发生循环依赖时,如果依赖的Bean被AOP代理,那么通过这个工厂获取到的就是代理后的对象,如果没有被AOP代理,那么这个工厂获取到的就是实例化的真实对象。
三、三级缓存是如何解决循环依赖问题的
典型回答
**Spring中Bean的创建过程其实可以分为两步:第一步实例化,第二步初始化
实例化的过程只需要调用构造函数把对象创建出来并给他分配空间,而初始化则是给对象的属性进行赋值。
而Spring之所以可以解决循环依赖就是因为对象的初始化是可以延后的,也就是说,当我创建一个Bean Service的时候,会先把这个对象实例化出来,然后再初始化其中的serviceB属性。
@Service
public class Service{
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB{
@Autowired
private ServiceA serviceA;
}
而 当一个对象只进行了实例化,但是还没有进行初始化时,我们称之为半成品对象。所以,所谓半成品对象,其实就是bean对象的空壳子,还没有进行属性注入和初始化。
当两个Bean在初始化过程中互相依赖的时候,如初始化A发现依赖了B,继续去初始化B,发现又依赖了A。大致流程如下图:
以上方法,就是通过引入三级缓存,解决了循环依赖的问题,在上述流程执行完成之后,ServiceA和ServiceB都被成功的完成了实例化和初始化。
以下是DefaultSingletonBeanRegistry#getSingleton方法,代码中,包括一级缓存、二级缓存、三级缓存的处理逻辑,该方法是获取bean的单例实例对象的核心方法:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先从一级缓存中获取bean实例对象,如果已经存在,则直接返回
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果一级缓存中不存在bean实例对象,而且当前bean正在创建中,则从二级缓存中获取bean实例对象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 如果二级缓存中也不存在bean实例对象,并且允许提前引用,则需要在锁定一级缓存之前,
// 先锁定二级缓存,然后再进行一系列处理
synchronized (this.singletonObjects) {
// 进行一系列安全检查后,再次从一级缓存和二级缓存中获取bean实例对象
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 如果二级缓存中也不存在bean实例对象,则从三级缓存中获取bean的ObjectFactory,并创建bean实例对象
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 将创建好的bean实例对象存储到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中移除bean的ObjectFactory
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}