- 什么是bean对象?
- bean的作用域
- 有状态的bean与无状态的bean
- singleton vs prototype
- Bean的循环依赖
- 什么是循环依赖?
- 三级缓存解决循环依赖
- 二级缓存可以么?
- Bean生命周期
由spring容器管理的对象统称为Bean对象。
Bean就是普通的java对象,和我们自己new的对象其实是一样的,只是这些对象是由spring去创建和管理的
有状态bean:
每个用户有自己特有的一个实例,在用户的生存期内,bean保存了用户的信息——即有状态;
一旦用户灭亡,bean的生命期也告结束。
无状态bean:
bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。
由于没有特定的用户,那么也就不能保持某一用户的状态——即无状态bean。
二者线程安全性
有状态就是有数据存储功能,可以保存数据——非线程安全
无状态就是一次操作不能保存数据。不能保存数据是不变类——线程安全
spring为什么要设置多种作用域?
应用中,有时候我们需要一个对象在整个应用中只有一个,而有些时候又希望对象每次使用的时候都重新创建一个,spring为了满足这些不同的需求,设置了不同的作用域方便我们使用
singleton vs prototype:
-
singleton单例——无状态bean
默认的作用域, Spring容器只存在一个共享的bean实例,每个用户获取得到的是同一个bean; -
prototype多例——有状态bean
每个用户从容器中获取bean都会为其创建一个独有的bean,可以保存其状态。
多个bean之间相互依赖,形成了一个闭环。
比如:A依赖于B、B依赖于C、C依赖于A。
代码中表示:
public class A{
B b;
}
public class B{
C c;
}
public class C{
A a;
}
三级缓存解决循环依赖
先看下三级缓存分别存放什么:
- singletonObjects:一级缓存,用于存放完整的 bean,从该缓存中取出的 bean 可以直接使用;
- earlySingletonObjects:二级缓存,存放提前暴露的 bean,bean 是只完成了实例化,未完成属性注入和初始化,用于解决循环依赖;
- singletonFactories:三级缓存,存放的是 Bean 工厂,主要用于生产 Bean,然后存放到二级缓存中。
Spring利用三级缓存解决循环依赖的过程:
1、实例化 A,此时 A 还未完成属性填充和初始化方法的执行,A 只是一个半成品。
2、为 A 创建一个 Bean 工厂,并放入到 三级缓存中。
3、发现 A 需要注入 B 对象,但是一级、二级、三级缓存均未发现对象 B。
4、实例化 B,此时 B 还未完成属性填充和初始化方法的执行,B 只是一个半成品。
5、为 B 创建一个 Bean 工厂,并放入到三级缓存中。
6、发现 B 需要注入 A 对象,此时在一级、二级未发现对象 A,但是在三级缓存中发现了对象 A,从三级缓存中得到对象 A,并将对象 A 放入二级缓存中,同时删除三级缓存中的对象 A。(注意,此时的 A 还是一个半成品,并没有完成属性填充和执行初始化方法)
7、将对象 A 注入到对象 B 中。
8、对象 B 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 B。(此时对象 B 已经是一个成品)
9、对象 A 得到对象 B,将对象 B 注入到对象 A 中。(对象 A 得到的是一个完整的对象 B)
10、对象 A 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 A。
Spring 在注入属性的时候是如何去获取依赖的呢?
当 Spring 为某个 Bean 填充属性的时候,它首先会寻找需要注入对象的名称,然后依次执行 getSingleton() 方法得到所需注入的对象,而获取对象的过程就是先从一级缓存中获取,一级缓存中没有就从二级缓存中获取,二级缓存中没有就从三级缓存中获取,如果三级缓存中也没有,那么就会去执行 doCreateBean() 方法创建这个 Bean。
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) {
// Bean 工厂中获取 Bean
singletonObject = singletonFactory.getObject();
// 放入到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
二级缓存可以么?
如果注入的是普通对象的话可以,但如果是代理对象的话不行。
因为如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,但是Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。二级缓存不满足,所以Spring 选择了三级缓存。
为什么代理对象时就需要在实例化之后立马为其创建代理?
这就和AOP有关,如果创建的 Bean 是有代理的,那么注入的就应该是代理 Bean,而不是原始的 Bean。
但是 Spring 一开始并不知道 Bean 是否会有循环依赖,在没有循环依赖的情况下,Spring 都会在完成填充属性,并且执行完初始化方法之后再为其创建代理。但是,如果出现了循环依赖的话,Spring 就不得不为其提前创建代理对象,否则注入的就是一个原始对象,而不是代理对象。因此,这里就涉及到应该在哪里提前创建代理对象?Spring 的做法就是在三级缓存中去提前创建代理对象。
文章目录
什么是循环依赖?
三级缓存解决循环依赖
二级缓存可以么?
什么是循环依赖?
多个bean之间相互依赖,形成了一个闭环。
比如:A依赖于B、B依赖于C、C依赖于A。
代码中表示:
public class A{
B b;
}
public class B{
C c;
}
public class C{
A a;
}
三级缓存解决循环依赖
先看下三级缓存分别存放什么:
singletonObjects:一级缓存,用于存放完整的 bean,从该缓存中取出的 bean 可以直接使用;
earlySingletonObjects:二级缓存,存放提前暴露的 bean,bean 是只完成了实例化,未完成属性注入和初始化,用于解决循环依赖;
singletonFactories:三级缓存,存放的是 Bean 工厂,主要用于生产 Bean,然后存放到二级缓存中。
Spring利用三级缓存解决循环依赖的过程:
1、实例化 A,此时 A 还未完成属性填充和初始化方法的执行,A 只是一个半成品。
2、为 A 创建一个 Bean 工厂,并放入到 三级缓存中。
3、发现 A 需要注入 B 对象,但是一级、二级、三级缓存均未发现对象 B。
4、实例化 B,此时 B 还未完成属性填充和初始化方法的执行,B 只是一个半成品。
5、为 B 创建一个 Bean 工厂,并放入到三级缓存中。
6、发现 B 需要注入 A 对象,此时在一级、二级未发现对象 A,但是在三级缓存中发现了对象 A,从三级缓存中得到对象 A,并将对象 A 放入二级缓存中,同时删除三级缓存中的对象 A。(注意,此时的 A 还是一个半成品,并没有完成属性填充和执行初始化方法)
7、将对象 A 注入到对象 B 中。
8、对象 B 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 B。(此时对象 B 已经是一个成品)
9、对象 A 得到对象 B,将对象 B 注入到对象 A 中。(对象 A 得到的是一个完整的对象 B)
10、对象 A 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 A。
Spring 在注入属性的时候是如何去获取依赖的呢?
当 Spring 为某个 Bean 填充属性的时候,它首先会寻找需要注入对象的名称,然后依次执行 getSingleton() 方法得到所需注入的对象,而获取对象的过程就是先从一级缓存中获取,一级缓存中没有就从二级缓存中获取,二级缓存中没有就从三级缓存中获取,如果三级缓存中也没有,那么就会去执行 doCreateBean() 方法创建这个 Bean。
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) {
// Bean 工厂中获取 Bean
singletonObject = singletonFactory.getObject();
// 放入到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
二级缓存可以么?
如果注入的是普通对象的话可以,但如果是代理对象的话不行。
因为如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,但是Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。二级缓存不满足,所以Spring 选择了三级缓存。
为什么代理对象时就需要在实例化之后立马为其创建代理?
这就和AOP有关,如果创建的 Bean 是有代理的,那么注入的就应该是代理 Bean,而不是原始的 Bean。
但是 Spring 一开始并不知道 Bean 是否会有循环依赖,在没有循环依赖的情况下,Spring 都会在完成填充属性,并且执行完初始化方法之后再为其创建代理。但是,如果出现了循环依赖的话,Spring 就不得不为其提前创建代理对象,否则注入的就是一个原始对象,而不是代理对象。因此,这里就涉及到应该在哪里提前创建代理对象?Spring 的做法就是在三级缓存中去提前创建代理对象。
Markdown 2090 字数 77 行数 当前行 74, 当前列 26 文章已保存20:05:44 HTML 2071 字数 61 段落
Bean生命周期核心的包括下面9个阶段:
阶段1:Bean元信息配置阶段
阶段2:Bean元信息解析阶段
阶段3:将Bean注册到容器中
阶段4:Bean Class加载阶段
阶段5:Bean实例化阶段
阶段6:属性赋值阶段
阶段7:Bean初始化阶段
阶段8:Bean的使用阶段
阶段9:Bean销毁阶段



