先来刷个LeetCode的入门题:
问题内容是:给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引。
比如:给定nums = [2, 7, 11, 15], target = 9 那么要返回 [0, 1],因为2 + 7 = 9
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
- 先去Map中找需要的数字,没有就将当前的数字保存在Map中,如果找到需要的数字,则一起返回。
- 三级缓存也是这种设计思想先去缓存里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。
核心类三级缓存:DefaultSingletonBeanRegistry.java
打个比方说实例化A的时候,先将A创建(早期对象)放入一个池子中。这个时候虽然。属性没有赋值,但是容器已经能认识这个是A对象,只是属性全是null而已。在populateBean方法中对属性赋值的时候,发现A依赖了B,那么就先去创建B了,又走一遍bean的创建过程(创建B)。同样也会把B的早期对象放入缓存中。当B又走到populateBean方法的时候,发现依赖了A,好吧,我们又去创建A呗,但是这个时候去创建A,发现我们在缓存能找到A(早期对象)了。就可以把B的A属性赋值了,这个时候B就初始化完成了。现在回到A调用的populateBean方法中。返回的就是B对象了,对A的B属性进行赋值就可以了。
- singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
- singletonFactories 映射创建Bean的原始工厂
- earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.
核心方法:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先检查一级缓存中是否存在
Object singletonObject = this.singletonObjects.get(beanName);
/**
* 如果一级缓存中不存在代表当前 Bean 还未被创建或者正在创建中
* 检查当前 Bean 是否正处于正在创建的状态中(当Bean创建时会将Bean名称存放到 singletonsCurrentlyInCreation 集合中)
*/
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
//检查二级缓存中是否存在
singletonObject = this.earlySingletonObjects.get(beanName);
/**
* @@如果二级缓存中不存在 并且 允许使用早期依赖
* allowEarlyReference : 它的含义是是否允许早期依赖
* @@那么什么是早期依赖?
* 就是当Bean还未成为成熟的Bean时就提前使用它,在实例化流程图中我们看到在添加缓存前刚刚实例化Bean但是还未依赖注入时的状态
*/
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) { //检查二级缓存中是否存在
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//获取三级缓存中的Bean ObjectFactory
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
//如果 Bean 对应的 ObjectFactory 存在
if (singletonFactory != null) {
//使用 getObject 方法获取到 Bean 的实例
singletonObject = singletonFactory.getObject();
//将 bean 从三级缓存提升至二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
流程图:
先说明前提:原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
原因很好理解,创建新的A时,发现要注入原型字段B,又创建新的B发现要注入原型字段A...
这就套娃了, 你猜是先StackOverflow还是OutOfMemory?
Spring怕你不好猜,就先抛出了BeanCurrentlyInCreationException