前面讲到Spring在创建Bean的过程中使用了三级缓存来对Bean对象进行缓存。这个三级缓存是用来解决对象的循环依赖问题。
什么是对象的循环依赖?
举个例子:
class A {
private B b;
private C c;
}
class B {
private A a;
}
class C {
private A a;
}
class A中引用了B和C,而B和C中又引用了A。
那么在spring中创建Bean对象时,需要先创建对象,然后对其属性进行填充。当对A进行属性填充时,发现需要创建B和C,而在创建B和C时,又发现需要创建A。此时就出现了循环依赖问题了。
怎么解决循环依赖?
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
spring利用singletonObjects, earlySingletonObjects, singletonFactories三级缓存去解决的。
我们假设现在有这样的场景AService依赖BService,BService依赖AService:
1. AService首先实例化,实例化通过ObjectFactory半成品暴露在三级缓存中
2. 填充属性BService,发现BService还未进行过加载,就会先去加载BService
3. 再加载BService的过程中,实例化,也通过ObjectFactory半成品暴露在三级缓存
4. 填充属性AService的时候,这时候能够从三级缓存中拿到半成品的ObjectFactory。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法。
getEarlyBeanReference这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例,这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象。
为什么是三级缓存?
一般情况下二级缓存已经足够解决循环依赖,但是如果遇到AOP的情况下,一个Bean需要被代理,这时二级缓存就无法解决了。
假设上面的AService配置了AOP,那么singletonFactory.getObject()返回的是一个AService的代理对象。因为AService是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象,假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象,这是不行的,因为AService是单例的。
所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。