首页 > 数据库 > 数据库开发技术 > 一个好汉三个帮—SpringBean的三级缓存

@Component
public class Cat {
private Dog dog;
// 通过构造方法进行注入
public Cat( Dog dog) {
this.dog = dog;
}
public void drink() {
System.out.println(dog);
}
}
@Component
public class Dog {
private Cat cat;
public Dog(Cat cat) {
this.cat = cat;
}
public void run() {
System.out.println(cat);
}
}
直接异常,异常提示如下图所示:

关键提示:Is there an unresolvable circular reference?
该异常表示发生了循环引用。
程序中,Cat类引用了Dog类对象,Dog类又引用了Cat类对象,对象间循环引用了!!!
But,Spring不是已经帮我们解决循环引用了吗。这怎么还会报错,难道我使用了假的的Spring?






1.注解注入不存在循环依赖异常问题
上面的测试中,使用构造法方法注入,存在循环依赖问题。
package com.qianfeng.circle;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
public class Cat {
@Resource
private Dog dog;
public void drink() {
System.out.println(dog);
}
}
package com.qianfeng.circle;
import org.springframework.stereotype.Component;
@Component
public class Dog {
@Resource
private Cat cat;
public void run() {
System.out.println(cat);
}
}
但是如果使用@Autowired或者@Resource修饰属性,是不会存在循环引用问题的。
spring:
main:
allow-circular-references: true
那么针对注解注入,Spring中是如何解决循环依赖问题的呢?答案就是Spring中使用到的三级缓存机制。
Spring Boot2.6.0后,默认不再支持循环依赖,我们可以通过如下配置可以开启:
spring:
main:
allow-circular-references: true
Spring中对三级缓存的对象的定义如下:

singletonObjects:一级缓存,存放完全初始化完成的对象
earlySingletonObjects:二级缓存,存放已创建,但是未填充属性的对象。
singletonFactories:三级缓存,存放提前暴露的对象。
(1)第一次加载配置时,通过getSingleton()方法获取cat对象(cat对象依赖dog对象),返回值是null。

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;
}
(2)创建cat对象

通过doCreateBean()方法中的createBeanInstance()方法创建cat的对象。

(3)创建cat对象后,通过addSingletonFactory(),将lambda表达式表示的singletonFactory对象加入Spring的三级缓存。

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)) {
// 将对应beanName的singletonFactory添加到三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 从二级缓存中删除指定beanName的对象
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
ObjectFactory是一个函数式接口:
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
(4)在populateBean()方法中,进行依赖对象的填充。本例中,填充cat对象依赖的dog对象。

(5)通过field.set()向cat对象注入dog对象,此时,dog对象其实还未创建。

(6)于是,通过getBean()获取dog对象,该方法调用doGetBean(),此时,dog对象并不存在。


(7)调用前面说的doCreateBean()方法创建dog对象。

(8)创建dog对象后,将dog对象对应的singletonFactory对象添加到Spring的三级缓存。

(9)调用populateBean(),填充dog对象中依赖的cat对象。

(10)cat对象在前面的逻辑中已经创建。通过getSingleton()获取cat对象,在getSingleton()方法中,cat对象被添加到Spring二级缓存,同时从三级缓存中删除cat对应的singletonFactory对象。

参考前面的getSingleton()源码。
执行getSingleton()中的singletonObject = singletonFactory.getObject()语句时,就会执行()
-> getEarlyBeanReference(beanName, mbd, bean)这个lambda表达式中的getEarlyBeanReference()方法。
getEarlyBeanReference()方法的源码如下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}(11)获取cat对象后,通过Field,向dog对象注入cat对象。

(12)注入cat对象后,dog对象初始化完毕,将dog对象添加到Spring的一级缓存。同时从二级和三级缓存中删除dog对应的对象。

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);
}
}
(13)dog对象初始化完成后,将dog对象注入cat对象,cat对象初始化完成。并且,初始化完成的cat对象也会添加到Spring的一级缓存。

以上源码是对注解注入时,解决循环依赖的分析。
那么,构造方法注入的循环依赖异常如何解决呢?其实很简单,在其中一个类的构造方法中,使用@Lazy修饰构造方法的参数即可。代码如下:
package com.qianfeng.circle;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
public class Cat {
private Dog dog;
// 使用懒加载注解@Lazy修饰构造方法的参数
public Cat(@Lazy Dog dog) {
this.dog = dog;
}
public void drink() {
System.out.println(dog);
}
}
package com.qianfeng.circle;
import org.springframework.stereotype.Component;
@Component
public class Dog {
private Cat cat;
public Dog(Cat cat) {
this.cat = cat;
}
public void run() {
System.out.println(cat);
}
}
如果你还有不明白的地方,可以在评论区或私信给我。关注Java架构栈,干货天天都不断哦。

原文作者:Java架构栈;如有侵权,请联系管理员删除。