循环依赖就是循环引用,就是两个或者多个 bean 相互之间的持有对方,最后形成一个环。例如 A 引用了 B,B 引用了 C,C 引用了 A。
解决循环依赖
原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常。通过构造器注入的循环依赖,也是无法解决的。
对于 setter 注入造成的依赖可以通过 Spring 容器提前暴露刚完成构造器注入但未完成其他步骤(如 setter 注入)的 bean 来完成。
Bean创建的几个关键点
Spring 创建Bean的过程,大致和对象的初始化有点类似吧。有几个关键的步骤
- createBeanInstance :实例化,此处要强调的是,Bean的早期引用在此出现了。
- populateBean : 填充属性,此处我们熟悉的@Autowired属性注入就发生在此处
- initializeBean : 调用一些初始化方法,例如init ,afterPropertiesSet
三级缓存
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
- singletonObjects:第一级缓存(成品缓存),里面存放的都是创建好的成品Bean。
- earlySingletonObjects : 第二级缓存(半成品缓存),里面存放的都是半成品的Bean。
- singletonFactories :第三级缓存(单例工厂缓存), 不同于前两个存的是 Bean对象引用,此缓存存的bean 工厂对象,也就存的是 专门创建Bean的一个工厂对象。此缓存用于解决循环依赖
创建流程
在AbstractBeanFactory.doGetBean()
方法中获取对象的方法如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 尝试通过bean名称获取目标bean对象,比如这里的A对象
Object sharedInstance = getSingleton(beanName);
// 我们这里的目标对象都是单例的
if (mbd.isSingleton()) {
// 这里就尝试创建目标对象,第二个参数传的就是一个ObjectFactory类型的对象,这里是使用Java8的lamada
// 表达式书写的,只要上面的getSingleton()方法返回值为空,则会调用这里的getSingleton()方法来创建
// 目标对象
sharedInstance = getSingleton(beanName, () -> {
try {
// 尝试创建目标对象
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
throw ex;
}
});
}
return (T) bean;
}
获取对象实例,如果对象不存在就通过getSingleton()
方法创建实例。这个方法里面有两个名称为getSingleton的方法,第一个getSingleton
是从缓存中查找bean,如果缓存未命中,则走第二个getSingleton
,尝试创建目标对象并注入依赖。先看第一个getSingleton
的源码:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 尝试从缓存中获取成品的目标对象,如果存在,则直接返回
Object singletonObject = this.singletonObjects.get(beanName);
// 如果缓存中不存在目标对象,则判断当前对象是否已经处于创建过程中,在前面的讲解中,第一次尝试获取A对象
// 的实例之后,就会将A对象标记为正在创建中,因而最后再尝试获取A对象的时候,这里的if判断就会为true
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 这里的singletonFactories是一个Map,其key是bean的名称,而值是一个ObjectFactory类型的
// 对象,这里对于A和B而言,调用图其getObject()方法返回的就是A和B对象的实例,无论是否是半成品
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 获取目标对象的实例
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
首先从一级缓存singletonObjects
获取目标对象,若不存在且目标对象被标记为创建中,从二级缓存earlySingletonObjects
中获取bean。如果不存在,继续访问三级缓存singletonFactories
,得到bean工厂对象,通过工厂对象获取目标对象。将目标对象放入二级缓存,删除三级缓存。
第二个getSingleton方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
// ......
// 调用 getObject 方法创建 bean 实例
singletonObject = singletonFactory.getObject();
newSingleton = true;
if (newSingleton) {
// 添加 bean 到 singletonObjects 缓存中,并从其他集合中将 bean 相关记录移除
addSingleton(beanName, singletonObject);
}
// ......
// 返回 singletonObject
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
该方法的主要逻辑是调用singletonFactory
的getObject()
方法创建目标对象,然后将bean放入缓存中。由于在doGetBean
中调用getSingleton
时第二个参数是用匿名内部类的方式传参,getObject
方法实际上调用的是createBean
方法,而createBean
又调用了doCreateBean
。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// ......
// ☆ 创建 bean 对象,并将 bean 对象包裹在 BeanWrapper 对象中返回
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 从 BeanWrapper 对象中获取 bean 对象,这里的 bean 指向的是一个原始的对象
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
/*
* earlySingletonExposure 用于表示是否”提前暴露“原始对象的引用,用于解决循环依赖。
* 对于单例 bean,该变量一般为 true。
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// ☆ 添加 bean 工厂对象到 singletonFactories 缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
/*
* 获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP
* 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回
* bean,所以大家可以把
* return getEarlyBeanReference(beanName, mbd, bean)
* 等价于:
* return bean;
*/
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
Object exposedObject = bean;
// ......
// ☆ 填充属性,解析依赖
populateBean(beanName, mbd, instanceWrapper);
// ......
// 返回 bean 实例
return exposedObject;
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 将 singletonFactory 添加到 singletonFactories 缓存中
this.singletonFactories.put(beanName, singletonFactory);
// 从其他缓存中移除相关记录,即使没有
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
方法的主要逻辑如下
- 用createBeanInstance创建目标对象
- 将对象添加到singletonFactories缓存中
- populateBean注入依赖
实例化过程如下:
关键点:
- A绑定到ObjectFactory 注册到工厂缓存singletonFactory中,
- B在填充A时,先查成品缓存有没有,再查半成品缓存有没有,最后看工厂缓存有没有单例工厂类,有A的ObjectFactory。调用getObject ,执行扩展逻辑,可能返回的代理引用,也可能返回原始引用。
- 成功获取到A的早期引用,将A放入到半成品缓存中,B填充A引用完毕。
- 循环依赖问题都解决了。