Bean 的创建过程
bean的创建过程会经历的几个方法

- 对于前两个方法
getbaen()
和doGetbaen()
来讲,主要是根据单例工厂去获取ioc三级缓存中的的bean对象,如果ioc容器中没有对应的bean对象,执行后面的创建过程。 这个过程是一个bean的工厂方法正在被调用,但该bean尚未完全初始化
java
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
java
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = this.transformedBeanName(name);
Object sharedInstance = this.getSingleton(beanName);// 根据单例获取ioc容器中的对象
Object beanInstance;
......
这里用到了双重检查锁的方式获取单例对象(不同的版本springboot版本这里结构不一样)
java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {// 第一重检查
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) { // 第二重检查
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
createBean()
和doCreateBean()
ioc容器中没有对应的bean对象时,就会去创建bean对象,正在被创建但尚未完成初始化createBeanlnstacne()
和populateBean()
第一个方法是基于反射的原理去创建bean对象,第二个是对该对象进行赋值处理。 bean完全初始化
java
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Creating instance of bean '" + beanName + "'");
}
...
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
...
java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
...
if (instanceWrapper == null) {
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
...
try {
this.populateBean(beanName, mbd, instanceWrapper);
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
...
}
如何找到getbean方法
找到AbstractApplicationContext
类,然后找到refresh()
方法
java
public void refresh() throws BeansException, IllegalStateException {}
在此方法中有一个初始化bean工厂的方法this.finishBeanFactoryInitialization(beanFactory);
这个方法中有一个 beanFactory.preInstantiateSingletons();
这样就可以找到getbean()
方法了
spring解决循环依赖
问题复现
定义两个类 A B 对象
java
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
java
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
我这里采用xml配置类的方式来注入bean
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="a" class="com.whj.pojo.A">
<property name="b" ref="b"></property>
</bean>
<bean id="b" class="com.whj.pojo.B">
<property name="a" ref="a"></property>
</bean>
</beans>
测试方法
java
public class testBean {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = context.getBean("a", A.class);
B b = context.getBean("b", B.class);
System.out.println(a);
System.out.println(b);
}
}

可以看到springboot已经解决了循环依赖的问题,那么是如何解决的呢?
三级缓存
所谓的三级缓存就是spring ioc容器,其本质就是一个map结构,只是不同的缓存所存放的内容不一样
首先找到DefaultSingletonBeanRegistry
类,类中定义了三个map结构的常量,分别表示不同的缓存结构
java
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);//一级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);//三级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);// 二级缓存
- 对于一级缓存
singletonObjects
而言string
表示添加bean的名称object
表示一个完整的bean 对象,也叫成品对象,当一个bean完全初始化后,它将被添加到这个缓存中。 - 对于二级缓存
earlySingletonObjects
而言string
表示添加bean的名称object
表示一个还未赋值的bean 对象,也叫半成品对象,当一个bean正在被创建但尚未完成初始化时,它将被添加到这个缓存中。 - 对于三级缓存
singletonFactories
而言string
表示添加bean的名称ObjectFactory<?>
是一个函数式接口,其中包含获取对象的逻辑,这通常是一个lambda表达式。该接口的实现是一个工厂方法,用于创建一个bean。当bean的工厂方法正在被调用,但该bean尚未完全初始化时,Spring将该工厂方法放入这个缓存中。
具体流程
第一次调用getbean()方法
首先调用getbean()方法进入doGetbaen()
然后从ioc缓存中获取A对象
此时
singletonObject
为空,并且this.isSingletonCurrentlyInCreation(beanName)
也返回false,表示这个对象不是在创建过程中,直接返回null

然后去创建bean对象 createBean()
和doCreateBean()
,经过createBeanlnstacne()
方法后,将保存的是用于创建单例对象a的工厂方法放入三级缓存中。只是一个lambda表达式。
然后进行初始化操作
this.populateBean(beanName, mbd, instanceWrapper);
第二次调用getbean()方法
由于具有循环依赖,即A对象中包含了B对象,需要对B对象初始化操作,故应该去创建B对象,所以需要重新调用getBean()方法去创建B

此时B对象也是第一次创建,所以和A刚开始的情况一样,通过双重检查锁去获取ioc容器中的对象时,直接返回null。
返回null

经历和对象A创建的过程一样, 经过createBean()
和doCreateBean()
,createBeanlnstacne()
方法后,将保存的是用于创建单例对象a的工厂方法放入三级缓存中。只是一个lambda表达式。

第三次调用getbean()方法
由于具有循环依赖,即B对象中包含了A对象,需要对A对象初始化操作,故应该去创建a对象,所以需要重新调用getBean()方法去创建a 此时对于a来说,是正在创建的过程中,所以对于双重检查锁的第一个判断,就能通过。
由于还未将对象放入二级缓存中,此时在二级缓存获取不到对象

继续往下通过三级缓存的lambda表达式去获取对象的实例
放入二级缓存,删除三级缓存,这时候对象a已经创建,但是还未初始化,是一个半成品对象
第四次调用getbean()方法
创建b的半成品对象 经历相同的操作以后 将b放入一级缓存中,删除二级缓存
将a放入一级缓存中,删除二级缓存
总结
-
第一次
getBean()
调用:- 容器检测到需要创建的bean A存在循环依赖。
- 容器创建A的半初始化实例,并将其提前暴露给其他bean。
- A的工厂方法可能被调用,引发对B的
getBean()
调用。
-
第二次
getBean()
调用:- 容器检测到需要创建的bean B存在循环依赖。
- 容器创建B的半初始化实例,并将其提前暴露给其他bean。
- B的工厂方法可能被调用,引发对A的
getBean()
调用。
-
第三次
getBean()
调用:- 容器检测到A的半初始化实例存在,直接返回给B的工厂方法,完成B的创建。
-
第四次
getBean()
调用:- 容器检测到B的半初始化实例存在,直接返回给A的工厂方法,完成A的创建。