三级缓存的方式解决
三级缓存用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储
从三个缓存中依次获取指定的Bean实例。 读取顺序依次是一级缓存–>二级缓存–>三级缓存
第一级缓存:用于存储单例模式下创建的Bean实例(已经创建完毕)
第二级缓存:早期曝光对象(如果被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象)
第三季缓存:早期曝光对象工厂(singletonFactories)
对于Spring循环依赖的情况总结如下:
- 不能解决的情况:1. 构造器注入循环依赖 2. prototype field属性注入循环依赖
- 能解决的情况:1. field属性注入(setter方法注入)循环依赖
「Spring的循环依赖的理论依据基于Java的引用传递」,当获得对象的引用时,「对象的属性是可以延后设置的」。
spring创建bean的流程:
对Bean的创建最为核心三个方法解释如下:
- createBeanInstance:例化,其实也就是调用对象的「构造方法」实例化对象
- populateBean:填充属性,这一步主要是对bean的依赖属性进行注入(@Autowired)
- initializeBean:回到一些形如initMethod、InitializingBean等方法
从对单例Bean的初始化可以看出,循环依赖主要发生在「第二步(populateBean)」,也就是field属性注入的处理。
三级缓存
这三级缓存分别指:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
// 从上至下 分表代表这“三级缓存”
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
...
/** Names of beans that are currently in creation. */
// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
// 它在Bean开始创建时放值,创建完成时会将其移出~
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Names of beans that have already been created at least once. */
// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
// 至少被创建了一次的 都会放进这里~~~~
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}
注:AbstractBeanFactory继承自DefaultSingletonBeanRegistry~
- singletonObjects:用于存放完全初始化好的 bean,「从该缓存中取出的 bean 可以直接使用」
- earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
- singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
获取单例Bean:
先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。「(如果获取到了就从」singletonFactories「中移除,并且放进」earlySingletonObjects「。其实也就是从三级缓存」移动(是剪切、不是复制哦~)「到了二级缓存)」
「加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决」
getSingleton()从缓存里获取单例对象步骤分析可知,Spring解决循环依赖的诀窍:「就在于singletonFactories这个三级缓存」。这个Cache里面都是ObjectFactory,它是解决问题的关键。
经过ObjectFactory.getObject()后,此时放进了二级缓存earlySingletonObjects内。这个时候对象已经实例化了,虽然还不完美,但是对象的引用已经可以被其它引用了
Spring的Bean创建过程
首先创建早期对象(还没有实例化的对象),然后添加到早期对象
首先创建Bean实例的包装类,方便后面使用
* 真正的创建bean实例(这里创建实例包装类),调用构造方法或者工厂方法
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
添加到我们的缓存中,这里就是我们的单利缓存池,解决循环依赖的关键所在
/**
* 添加到我们的单例工厂中缓存起来
*
* 提早暴露早期对象 (还没有进行初始化的对象) 就是还没有进行赋值的对象就是早期对象
*
*/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
然后就是Bean的赋值,初始化Bean
/**
* 给我们的Bean进行赋值
*/
populateBean(beanName, mbd, instanceWrapper);
/**
* 初始化bean
*/
exposedObject = initializeBean(beanName, exposedObject, mbd);
一级缓存,singletonObjects存的是完成创建的对象,也就是实例化了的对象。
二级缓存是earlySingletonObjects,存储早期对象
三级缓存是singletonFactory,在creatBeanInstance时候放入
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
/**
* singletonObjects 这个就是我们大名鼎鼎的单例缓存池
*/
Object singletonObject = this.singletonObjects.get(beanName);
/**
* singletonObject == null 并且单例正在创建
*
* isSingletonCurrentlyInCreation就是个标识,标识这个bean是否正在被创建。
*/
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;
}
如果没捞到,那继续去父工厂中捞一波,如果拿到就返回。
/**
* 如果缓存中没有拿到,就去检查父工厂里面有没有
*/
BeanFactory parentBeanFactory = getParentBeanFactory();
如果还是没有,那只能自己创建对象了
/**
* 真正的创建bean实例了,调用构造方法或者工厂方法
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
把早期对象添加到我们的单例缓存池中
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
/**
* singletonObjects 单例缓存池
* singletonFactories 单例工厂
* earlySingletonObjects 早期单例对象
*/
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
二级缓存作用
A放入三级缓存中是一个factory对象,是因为spring目前还无法判断,A对象在后续是否存在循环依赖,所以factory对象的目的在于,如果发生了循环依赖,则需要创建被增强的对象,而为什么又是被增强的对象呢,因为,我记得spring对bean的增强统一在后置处理中,但是由于被依赖,所以必须提前创建好被增强的对象,放入到二级缓存中。否则在程序运行中是代理对象A,而B拿到的对象却是普通对象A。
但是那跟放入二级缓存有啥关系呀,那是因为,如果不放入二级缓存中,那只能还是从三级缓存中获取,那么会再次触发aop增强
多一级缓存意义就是在bean循环引用过程中,发生了AOP增强,避免返回真实对象而非代理对象Proxy
spring为什么不支持构造器循环依赖
因为A中构造器注入了B,那么A在关键的方法addSingletonFactory()之前就去初始化了B,导致三级缓存中根本没有A,所以会发生死循环,Spring发现之后就抛出异常了。至于Spring是如何发现异常的呢,本质上是根据Bean的状态给Bean进行mark,如果递归调用时发现bean当时正在创建中,那么久抛出循环依赖的异常即可