Bean 的创建过程
bean的创建过程会经历的几个方法
- 对于前两个方法
getbaen()和doGetbaen()来讲,主要是根据单例工厂去获取ioc三级缓存中的的bean对象,如果ioc容器中没有对应的bean对象,执行后面的创建过程。 这个过程是一个bean的工厂方法正在被调用,但该bean尚未完成创建
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
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版本这里结构不一样)
@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完全初始化
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);
...
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()方法
public void refresh() throws BeansException, IllegalStateException {}
在此方法中有一个初始化bean工厂的方法this.finishBeanFactoryInitialization(beanFactory);
这个方法中有一个 beanFactory.preInstantiateSingletons();这样就可以找到getbean() 方法了
spring解决循环依赖
问题复现
定义两个类 A B 对象
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
我这里采用xml配置类的方式来注入bean
<?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>
测试方法
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已经解决了循环依赖的问题,那么是如何解决的呢?
为何需要三级缓存?
循环依赖指的是多个bean之间存在相互依赖的情况,形成了一个循环。在这种情况下,如果不采取特殊的机制,很容易导致初始化时的死锁或者无法正确解析依赖关系,比如在上述的例子中,类A将类B作为一个变量,而类B又将类A作为一个变量,简单理解就是A中有B,B中有A,这样就是一个循环依赖的具体展现,当然也可以是多个类之间的依赖。三级缓存的引入就解决了循环依赖问题,确保bean的创建和初始化过程能够正确进行。
三级缓存
所谓的三级缓存就是spring ioc容器,其本质就是一个map结构(singletonObjects、earlySingletonObjects、singletonFactories),只是不同的缓存所存放的内容不一样
首先找到DefaultSingletonBeanRegistry类,类中定义了三个map结构的常量,分别表示不同的缓存结构
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()方法后(反射的方式创建bean),布尔值earlySingletonExposure判断是否符合循环依赖,如果符合就经过addSingletonFactory() 方法,将用于创建单例对象a的工厂方法放入三级缓存中。这只是一个lambda表达式。
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
然后进行初始化操作
this.populateBean(beanName, mbd, instanceWrapper);
第二次调用getbean()方法
由于具有循环依赖,调用populateBean()方法对A初始化时(也就是给A对象的属性进行赋值的时候),发现A对象中包含了B对象,需要对B对象初始化操作,故应该去创建B对象,所以需要重新调用getBean()方法去创建B
此时B对象也是第一次创建,所以和A刚开始的情况一样,通过双重检查锁去获取ioc容器中的对象时,直接返回null。
返回null
经历和对象A创建的过程一样, 经过createBean()和doCreateBean() ,createBeanlnstacne()方法后,判断是否符合循环依赖以后,满足条件就将用于创建单例对象a的工厂方法放入三级缓存中。只是一个lambda表达式。(和将A对象放入三级缓存中是一样的步骤)
第三次调用getbean()方法
由于具有循环依赖,即B对象中包含了A对象,需要对A对象初始化操作,故应该去创建a对象,所以需要重新调用getBean()方法去创建a
此时对于a来说,是正在创建的过程中,所以对于双重检查锁的第一个判断,就能通过。
由于还未将对象放入二级缓存中,此时在二级缓存获取不到对象
判断三级缓存是否有A对象
继续往下通过三级缓存的lambda表达式去获取对象的实例
放入二级缓存,删除三级缓存,这时候对象a已经创建,但是还未初始化,是一个半成品对象
由于a是一个半成品对象,通过getSingleton() 方法直接返回给B的工厂方法,完成B的创建(不同的版本这里实现的不一样)
B创建完成以后将二级缓存的数据移除,添加到一级缓存。
第四次调用getbean()方法
完成对A对象的创建,需要判断b对象是否创建
获取容器中b对象
一级缓存存在B对象,返回给A的工厂方法,完成A的创建
将a放入一级缓存中,删除二级缓存
三级缓存是在哪解决的?
通过四次getBean方法的调用,可以很明显知道解决三级缓存是在bean的创建过程中,但是哪一个方法最能体现呢?
答案是:AbstractAutowireCapableBeanFactory 类中的doCreateBean()方法
在这个方法中,Spring 容器进行了一系列的处理,包括实例化 bean、依赖注入、初始化方法的调用等。 核心的方法就是
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
检查是否有循环依赖,如果有,就将半成品bean对象实例放入三级缓存中。
总结
-
第一次
getBean()调用:- 容器检测到需要创建的bean A存在循环依赖。
- 容器创建A的半初始化实例,并将其提前暴露给其他bean。
- A的工厂方法可能被调用,引发对B的
getBean()调用。
-
第二次
getBean()调用:- 容器检测到需要创建的bean B存在循环依赖。
- 容器创建B的半初始化实例,并将其提前暴露给其他bean。
- B的工厂方法可能被调用,引发对A的
getBean()调用。
-
第三次
getBean()调用:- 容器检测到A的半初始化实例存在,直接返回给B的工厂方法,完成B的创建。
-
第四次
getBean()调用:- 容器检测到B的半初始化实例存在,直接返回给A的工厂方法,完成A的创建。