本文主要研究getBean的流程。
1.首先尝试框架会从容器的缓存里获取单例Bean实例,这个单例Bean有可能是一个普通Bean,也有可能是一个FactoryBean,然后调用他的getObject方法返回。
2.如果这里1获取不到,不管是单例还是原型模式,都要框架另外创建实例了。然后进行循环依赖的判断逻辑,正是因为循环依赖,才会用到三级缓存,
3.如果该容器中没有该Bean的BeanDefiniton实例,则递归去父容器去查找。
4.如果该容器中有该Bean的BeanDefiniton实例,则获取Bean实例。
5.递归实例化显式依赖的Bean。显式依赖在其BeanDefiniton设置了Depends-On属性。假设A Depends - On B,那么B 就会先于A实例化。
6.根据不同的Scope选择不同的策略创建Bean实例。
7.对Bean经行类型检查。
下面直接看看代码吧
这行代码直接返回Bean的名字,这里支持别名获取,和加了&前缀获取生产的Bean的工厂类本身。
然后尝试从缓存中获取Bean实例。
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//尝试从一级缓存获取Bean实例,singletonObjects就是单例Bean的一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
//如果完整的Bean实例美与被创建出来再一级缓存,那么就会将其加入到SingletonCurrentlyInCreation中
//如果一级缓存没有,且这个Bean正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//对一级缓存加锁,防止几个线程对缓存同时 操作,导致数据对不上。
synchronized (this.singletonObjects) {
//尝试从二级缓存中获取Bean实例,注意这里的二级和后面的三级缓存不是ConcurrentMap,具体原因自己思考吧
singletonObject = this.earlySingletonObjects.get(beanName);
//如果二级缓存也没有,且存在早期引用,说明是循环依赖。
if (singletonObject == null && allowEarlyReference) {
//从三级缓存获取创建Bean实例的工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果哦从三级缓存获取到的工厂 不为空,则从工厂里面获取Bean实例
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//放到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存删除,保证缓存的一致性
//注意:只有一级缓存保存的是完整的Bean.
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
到了这里,逻辑上差不多结束了。然后Spring会根据FactoryBean前缀判断到底需要获取 的是工厂类还是.Bean本身。
以上逻辑是单例Bean的获取,和从缓存中获取到实例的情况。如果没有呢,那么就会继续就会执行下面的逻辑。
// 可以看到原型模式也有一个注册列表,如果此时还在注册表中,说明是循环依赖,原型模式的循环依赖误无解,直接抛出异常。而这里的注册表是ThreadLocal对象,也就是说在一个线程中出现了循环依赖,无解。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
如果从当前容器中找不到这个Bean的BeanDefinition,且该容器存在父容器,则递归从父容器寻找。
找到BeanDefiniton,如果BeanDefiniton里面具有显示的循环依赖,抛出异常 。
然后根据不同的Scope的Bean创建。
创建完成后,在一级缓存中加入Bean实例,并且在二级三级删除。在已注册名单中加入这个Bean.正创建名单中清楚这个Bean