个人理解: a(bean) 实例化的时候去缓存(一级->二级-> 三级)中找,没有就将工厂bean放到三级缓存,实例化完成后放到二级缓存同时删掉三级缓存中的a,然后赋值属性初始化,如果没有循环依赖初始化完成,便删除二级缓存中的半成品,将完整对象当到一级缓存(单例池中),完成初始化
bean的声明周期
-
beanDefintionReader: 配置读取
-
BeanDefintionBean: 配置的解析
-
beanFactorypostProcessor: bean的扩展方法(实例化前执行,操作元数据)
…(此期间便是实例化过程)
-
populateBean: 填充Bean的属性
-
aware:执行实现了此接口的方法
-
Beanpostprocessor:before:bean的前置增强
-
int method: 初始化里面的方法
-
BeanPostProcessor:after: bean的后置增强
bean的循环依赖
属性的初始化基本上使用的是两种,一种set,,一种构造函数,所以spring中循环依赖问题set方式初始化时可以解决的,构造函数解决不了.
解决方式
将半成品(也可以说是获取半成品bean的方法或工厂对象)放到三级缓存中(二级缓存存放的是属性没有赋值的bean,这次才是真正的半成品,一级缓存存放的成品bean,专有名词是单例池),ioc(包含单例池)没有就去二三级缓存中找
1.三级缓存(专有名词:对象提前暴露):
- singletonObjects:一级缓存
- earlySingletonObjects: 二级缓存
-
singletonFactories: 三级缓存
中ObjectFactory是一个函数式接口,仅有一个方法,可以传入lambda表达式,,可以是匿名内部类,通过调用getObject方法来执行具体的逻辑
执行流程
1.配置文件的bean被加载但是还没有实例化
2.会先实例化a
-
bd.isAbstract: 是否是抽象的 false
-
bd.isSingleton():是否是单例的 true
-
bd.isLazyInit(): 是否是懒加载 false
2.1 进一步执行
2.2.执行getBean() –> doGetBean()
2.3.getSingleton() 去容器中查找
2.3.1.调用里面的this.singletonObjects.get(beanName) [ 一级缓存 ]
2.4.刚开始一级缓存中是找不到的,因为还没有被创建,所以要调用后面的createBean() -> doCreateBean() 方法
此处的creatBean(),便是执行的(
需要存到三级缓存中的objectFunction中的getObject
)中的getObject
2.5执行singletonFactory.getObject()方法**
2.5.1执行creatBean()
2.5.2完成bean的实例化(反射)
2.5.3完成A的实例化,但是b还没有赋值,是个半成品
2.5.4此方法beanName 此时传的是a,但是函数不会执行,只有调用get时才会执行
2.5.5查看一级缓冲是否有a,没有就像三级缓存中放入a,
其中beanName是此时的bean名称a,singletonFactory便是上一步传过来的函数
三级缓存中的数据格式: k:a,v:lambda 表达式
2.5.6填充属性
此时的填充属性是b
填充进一步执行
此时的originalValue 是
运行时的bean引用
并不是b对象
然后会执行getBean -> doGetBean -> getSingleton -> 查找B
3 实例化B
流程与a相同,createBean -> doCreateBean -> createBeanInstance() -> populateBean() (填充属性 a) -> 去容器中找
赋值a属性
-
此时一级缓存中没有a(实例化a的过程中,是将a放入了三级缓存中)[ this.singletonObjects() 返回null ]
-
此时a也在创建中(因为我们在a实例化的过程中引用了b属性,所以要在实例化及初始化b后,才能完成a的实例化及初始化) [ isSingletonCurrentlyInCreation 返回true ]
-
向二级缓存中查找,返回null
-
向三级缓存中查找,此时执行的便是三级缓存中存储的lambda表达式[ () -> getEarlyBeanReference 方法 ],
在三级缓存中获取到的对象,并不是完成对象而是半成品对象,
,此时 放入二级缓存中[ 格式: k: a , v: A@1755 ]
下一步便是删除三级缓存中的a,然后返回a,
4.初始化B
将B中的a属性赋值,但是此时a属性中的b属性还是null
,此时B的实例化和初始化完成
此时a的状态还是半成品,b是完整对象
向一级缓存中放入b,同时删除二级及三级缓存中的b删除
5.初始化A
然后将a中的b属性赋值
,
现在a便是成品状态
将a放入一级缓存,同时删除二级及三级缓存中的a
6.返回完成A的实例化和初始化,开始实例化和初始化B(流程与a一样,前提是一级缓存中没有)
下一步,实例化和初始化B,会去一级缓存中找,此时一级缓存中就会有b,取到直接返回,不用再次重新实例化和初始化
7.bean实例化和初始化完成
2.三级缓存解决循环依赖问题的关键是什么?为什么通过提前暴露对象能解决?
实例化和初始化分开操作,在中间过程中给其他对象赋值的时候,并不是一个完整对象,而是把半成品赋值给了其他对象。
3. 如果只使用一级缓存能不能解决问题?
不能,在整个处理过程中,缓存中放的是半成品和成品对象,如果只有一级缓存,那么成品和半成品都会放到一集缓存中,有可能在获取过程中获取到半成品对象,此时半成品对象无法使用,不能直接进行相关处理,因此要把半成品和成品的存放空间分割开.
4.只使用二级缓存行不行,为什么需要三级缓存?如果我能保证,所用的bean对象都不去调用getEarlyBeanReference从方法,使用二级缓存可以吗?
使用三次缓存是因为要调用 getEarlyBeanReference
如果能保证所有的bean都不调用getEarlyBeanReference,可以只使用二级缓存.
5.getEarlyBeanReference做了什么工作?
使用getEarlyBeanReference(三级缓存)的本质在于解决aop的代理问题!如果需要代理,就想原来的对象覆盖
6.如果某个bean需要代理对象,那么会不会创建普通的bean对象?
必须会!,不管需不需要代理,bean一定会被创建的,但是会不会执行代理流程是不一定的 [ 不配置aop就不会代理 ]
7.为什么使用了三级缓存就可以解决这个问题?
(本质在于解决aop的代理问题!)
当一个对象需要被代理的时候,在整个创建过程中,是包含两个对象。一个是普通对象,一个是代理生成的对象,bean默认都是单例的,那么我在整个生命周期的处理环节中,一个beanname能对应两个对象吗?不能,既然不能,保证我在使用的时候加一层判断,判断一下是否需要进行代理的处理。
8,我怎么知道你什么时候使用
因为不知道什么时候会调用,所以通过一个匿名内部类的方式,在使用的时候直接对普通对象进行覆盖操作,保证全局唯一!
beanFactory 属性初始化及扩展工作
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{
//有参构造
public MyClassPathXmlApplicationContext(String... configLocaltions){
super(configLocaltions);
}
//属性值扩展
@Override
protected void initPropertySources(){
getEnvironment().setRequiredProperties("OS");
}
//扩展 beanFactory
@Override
protected void customizeBeanFactory(DeffaultListableBeanFactory beanFactory){
//是否允许覆盖同名称的不同定义的对象
super.setAllowBeanDeffinitionOverriding(false);
//是否允许bean之间的循环依赖
super.setAllowCircularReferences(false);
//没有此行,扩展的东西不会生效
super.customizeBeanFactory(beanFactory);
}
}