前言
Spring不能解决构造器的循环依赖,但是可以解决(单例)属性的循环依赖。
Bean创建流程
Spring创建bean过程中,跟循环依赖有关的几个流程如下:
-
尝试从缓存中进行获取bean
-
bean实例化
-
提前暴露
-
bean属性赋值
-
bean初始化
-
加入缓存
三级缓存的妙用
Spring通过三级缓存解决循环依赖问题。这三级缓存分别是指:
- singletonObjects:单例对象的缓存
- earlySingletonObjects:提前曝光的单例对象的缓存
- singletonFactories:单例对象工厂的缓存
那么这三级缓存又是如何在Spring的Bean创建流程中发挥作用的呢?且看
关键点一
过程1中,在get一个bean之前,会先尝试从缓存中直接进行获取。
具体代码:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中,会调用 getSingleton 方法。
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;
}
分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就通过singletonFactory.getObject()方法从三级缓存中获取,如果获取到了,则将其从三级缓存中移除,并放入二级缓存中。
关键点二
过程3中,当实例化一个bean之后,满足一定条件(单例 && 允许循环依赖 && 当前bean正在创建中),则会将其提前暴露(加入三级缓存中)
具体代码:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,会调用addSingletonFactory方法
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);
}
}
}
该方法会将其加入到三级缓存中,并且从二级缓存中移除。也就相当于将其提前曝光了。
关键点三
过程4中,如果要注入的依赖(如 被@Autowired修饰)还未创建,则会调用getBean方法进行创建
关键点四
过程6中,当该bean初始化完成后,会将其加入一级缓存中
具体代码:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) 方法会调用 addSingleton 方法
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加入到一级缓存中,并从二级缓存、三级缓存中移除。
举例
通过以上的流程,就能解决三级缓存问题了。这里举一个例子详细说明一下。
以A、B两个类通过@Autowired注解相互依赖的例子来说,假设先创建A类:
- A类进行实例化
- A类提前暴露,将A类加入三级缓存中
- A类属性赋值,发现依赖B类,开始B类的创建
- B类进行实例化
- B类提前暴露,将B类加入三级缓存中
- B类属性赋值,发现依赖A类,开始A类的创建
- 在创建A类时,发现可以通过三级缓存获取到A类,就将A类从三级缓存中移除,并加入二级缓存中,返回A类
- B类进行初始化
- B类完成创建,将自己加入到一级缓存中,并从二级缓存、三级缓存中移除
- A类获取到B类依赖后,完成属性赋值,继续进行实例化
- A类完成创建,将自己加入到一级缓存中,并从二级缓存、三级缓存中移除
思考
看完上述内容,仔细想一想,其实一级缓存就可以解决循环依赖问题了。
那么Spring为什么要用三级缓存呢?
下一篇文章,笔者将介绍 Spring AOP代理原理 ,告诉你答案。