spring中的循环引用
————————————————
在博客上研究了shadow?s大神的pring源码关于循环引用的笔记,自己根据源码进行学习,在此记录一下自己的笔记。
大神的博客地址:https://blog.csdn.net/java_lyvee/article/details/101793774
首先说一下,spring在单例的情况下是默认支持循环引用的
1.spring在创建bean的时候会先创建这个bean的对象,第一次调用AbstractBeanFactory的doGetBean方法中的getSingleton方法看单例池中是否有这个bean,并且看用于记录正在创建的set集合中是否有这个对象,没有就返回继续创建对象
* 这里是spring第一次调用getSingleton
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先从单例池中根据名字获取这个bean,单例池是一个Map类型
Object singletonObject = this.singletonObjects.get(beanName);
//判断如果为空并且有正在创建中的对象,也就是进行第二次getSingleton时候往set集合中放入了值就继续往下,否则直接返回null
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//首先从第三个map中根据名字获取对象,earlySingletonObjects临时存放创建的对象
singletonObject = this.earlySingletonObjects.get(beanName);
//如果没有获取到并且是允许循环依赖的就继续
if (singletonObject == null && allowEarlyReference) {
//从第二个map中根据名字获取对象工厂,singletonFactories里面是工厂,拥有临时存放的对象,并且能够对对象做一些改变
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//调用doCreateBean方法,创建对象
singletonObject = singletonFactory.getObject();
//把单例对象放入第三个map中临时存储
this.earlySingletonObjects.put(beanName, singletonObject);
//从第二个map中移除对象
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
2.调用第二次getSingleton方法,在DefaultSingletonBeanRegistry这个类中,这时候会把对象放入一个set集合中,用于记录正在创建的对象
*this.singletonsCurrentlyInCreation.add(beanName)这里就是放到set集合中
*/
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
3.调用AbstractAutowireCapableBeanFactory类的doCreateBean方法继续创建bean,根据容器中的allowEarlyReference属性以及是否单例,以及是否是正在创建的对象来判断是否能够临时缓存到map中
/**
*
* 此处就是创建一个bean的入口
* 这里调用AbstractAutowireCapableBeanFactory的doCreateBean方法
*
*/
singletonObject = singletonFactory.getObject();
newSingleton = true;
/**
*
* 这里判断是否是单例,是否支持循环引用,是否正在创建中
*
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//这里是把创建出来的对象放入map中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//填充属性,也就是自动注入
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//首先判断单例池中是否有这个bean
if (!this.singletonObjects.containsKey(beanName)) {
//没有,就把此对象放入第二个map中临时存储
this.singletonFactories.put(beanName, singletonFactory);
//把此对象从第三个map中移除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
4.allowEarlyReference属性在spring中默认为true,默认会把对象缓存到map中供后续获得。当然这个属性spring也提供了Api来进行修改,
annotationConfigApplicationContext.setAllowCircularReferences(false)
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(AppConfig.class);
annotationConfigApplicationContext.setAllowCircularReferences(false); //关闭循环依赖支持
//初始化spring容器的环境
annotationConfigApplicationContext.refresh();
5.当创建的对象A走生命周期走到需要注入属性对象B的时候,spring就会去创建对象B,B同样与上面一样走流程,当走到需要注入属性对象A的时候,也会去调用getSingleton方法,这时候就能够从第二个map中获取到A对象,(为什么是第二个map,是因为里面是工厂,我们可以改变创建的对象来满足一些需求,比如说AOP)因为这时候在set集合中有这个对象A,注入B中后继续走完生命周期,完成bean的创建
6.B创建完成之后,容器中就有了B这个bean,这样A也能完成生命周期,完成bean的创建
解释一下三个map:
* 单例池map叫做singletonObjects,第一个map
* 存放临时对象的map叫做singletonFactories,不过这里存放的是一个工厂,可以通过一些方式来改变bean,比如Aop,第二个map
* 还有另一个存在临时对象的map叫做earlySingletonObjects 这里存放的是一个临时对象,第三个map
解释一下为什么要存在第二个map中,而不是作为对象直接存在第三个map中:
*第二个map中的工厂,主要调用了以下这个方法
*
* 用AOP举例
* spring 实例化A,然后发现需要注入B,这时候A还没有完成代理,
* spring去实例化B,发现需要注入A,就去调用getSingleton,拿到没有完成代理的A
* 这是就有问题了,A是一个没有完成代理的对象,这跟我们需要的不一样,就错了
* 因此需要第二个map,也就是里面的工厂来做处理,帮我们完成AOP代理并放到第三个map中
* 这样我们就能拿到正确的代理的A了
*
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}