Spring源码--02--三级缓存 解决 循环依赖

Posted 高高for 循环

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码--02--三级缓存 解决 循环依赖相关的知识,希望对你有一定的参考价值。

循环依赖

N个Bean互相引用对方,最终形成闭环。

在这里插入图片描述

前提保证:

创造的所有对象都是单例对象.

循环依赖的问题:

如果是通过构造器的方式, 那么没办法解决.

如果是通过set的方式, 那么可以使用三级缓存解决此问题

案例:

class A

package springdemo.cy.bean;

public class A {

    private  B b;

    public A(B b) {
        this.b = b;
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

class B

package springdemo.cy.bean;

public class B {

    private A a;

    public B(A a) {
        this.a =a;
    }

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    public  void  show(){
        System.out.println("B.....show");
    }
}

test.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="springdemo.cy.bean.A">
     <property name="b" ref="b" ></property>
    </bean>


    <bean id="b" class="springdemo.cy.bean.B">
         <property name="a"  ref="a"></property>
    </bean>

</beans>

ref元素是将目标Bean定义的实例注入到属性或构造函数中

1.通过构造函数进行对象的创建

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import springdemo.cy.bean.B;

public class test {

    public static void main(String[] args) throws Exception {

    ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");

        B bean = context.getBean(B.class);

        bean.show();
        System.out.println(bean);

    }

}

在这里插入图片描述

2.通过set方式进行对象的创建

在这里插入图片描述
在这里插入图片描述

bean的生命周期

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

三级缓存 DefaultSingletonBeanRegistry

DefaultSingletonBeanRegistry类:

在这里插入图片描述

在这里插入图片描述

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	// 从上至下 分表代表这“三级缓存”
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
	...
	
	/** Names of beans that are currently in creation. */
	// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
	// 它在Bean开始创建时放值,创建完成时会将其移出~
	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	/** Names of beans that have already been created at least once. */
	// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
	// 至少被创建了一次的  都会放进这里~~~~
	private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}

一级缓存: singletonObjects

用于存放完全初始化好的 bean对象,从该缓存中取出的 bean 可以直接使用

二级缓存: earlySingletonObjects

提前曝光的单例对象的cache,存放完成实例化但未初始化 bean 对象(尚未填充属性),用于解决循环依赖

三级缓存:singletonFactories:

单例对象工厂的cache,存放 bean 工厂对象(lambda表达式),用于解决循环依赖

三级缓存读取顺序:

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//从一级缓存获取
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果一级缓存中没有,并且已标记这个bean正在被定义
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				//从二级缓存获取bean
				singletonObject = this.earlySingletonObjects.get(beanName);
				//如果二级缓存也拿不到 去三级缓存拿
				if (singletonObject == null && allowEarlyReference) {
					//从三级缓存取值
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						//如果三级缓存存在值,将三级缓存中的缓存移除,加入二级缓存
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}


在这里插入图片描述

  1. 先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
  2. 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
  3. 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。获取不到 return null
  4. 如果获取到了就从singletonFactories中移除,并且放进二级缓存earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存.然后再返回获取到的这个对象

Spring解决循环依赖的诀窍:就在于singletonFactories这个三级缓存。

前提:

  • 加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

在这里插入图片描述

1.singletonFactories这个三级缓存这个Cache里面都是ObjectFactory实例,它是解决问题的关键

2.经过ObjectFactory.getObject()获取对象后,此时放进了二级缓存earlySingletonObjects内。这个时候对象已经实例化了但未初始化,(尚未填充属性)虽然还不完美,但是对象的引用已经可以被其它引用了。

debug 源码

在这里插入图片描述

ClassPathXmlApplicationContext

在这里插入图片描述

AbstractApplicationContext

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

DefaultListableBeanFactory

在这里插入图片描述

DefaultListableBeanFactory 继承 DefaultSingletonBeanRegistry

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

此过程中先创建A对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

AbstractBeanFactory

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

进入 getSingleton(beanName) 方法;

父类DefaultSingletonBeanRegistry的中方法 在这里插入图片描述

DefaultSingletonBeanRegistry

在这里插入图片描述

在这里插入图片描述

一级缓存 : singletonObjects

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

一路返回 到AbstractBeanFactory

AbstractBeanFactory

在这里插入图片描述
在这里插入图片描述

还是DefaultSingletonBeanRegistry中 getSingleton方法 只是形参不一样

在这里插入图片描述

getSingleton方法参数之一是ObjectFactory实例

实例就必须重写抽象的getObject方法 ,spring是用lambda表达式的形式

在这里插入图片描述

lambda表达式在这里插入图片描述

在这里插入图片描述

DefaultSingletonBeanRegistry

在这里插入图片描述

此时一级缓存里还没有a对象

在这里插入图片描述
在这里插入图片描述

AbstractAutowireCapableBeanFactory

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

createBeanInstance(beanName, mbd, args); 方法

这个方法里面是利用反射创建好 空属性 或者默认属性的a对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

addSingletonFactory(String , ObjectFactory<?> ) {}

DefaultSingletonBeanRegistry里面的方法

在这里插入图片描述

如果一级缓存没有对象在这里插入图片描述

清空二级缓存在这里插入图片描述

set集合添加bean对象的名字在这里插入图片描述

一组已注册的单例,按注册顺序包含bean名称的set集合在这里插入图片描述

此时三级缓存里 放的是lambda表达式的实例

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

进入 populate()方法 做属性填充

AbstractAutowireCapableBeanFactory

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

BeanDefinitionValueResolver

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

因为a对象的属性b,是一个对象,

所以此时要从beanFactory.getBean(String name)获取b对象

开始循环

开始循环 1

AbstractBeanFactory

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此时a,b两个对象都已实例化完成,但都还未初始化

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

往三级缓存map 添加元素

在这里插入图片描述
在这里插入图片描述

进入 populateBean(beanName, mbd, instanceWrapper);

给b对象填充属性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此时a,b对象都已实例化,但都未初始化 属于创建过程中

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

显然此时二级缓存是获取不到a的,所以singletonObject == null

在这里插入图片描述
在这里插入图片描述

此时三级缓存map存放着 {key = a ,value= lambda表达式的实例 }在这里插入图片描述

获取ObjectFactory<?> 类型的 lambda表达式实例,然后调用getObject()方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

往二级缓存map添加元素 ,

(key =beanName ,value =a对象 (此时a对象只实例化 未初始化)}

在这里插入图片描述

在这里插入图片描述

从三级缓存map里清除, key=a 的元素 ,

三级缓存读取的顺序

在这里插入图片描述

  1. 先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
  2. 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
  3. 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。获取不到
    return null
  4. 如果获取到了就从singletonFactories中移除,并且放进二级缓存earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存.然后再返回获取到的这个对象

此时

  • 一级缓存 在这里插入图片描述
  • 二级缓存 在这里插入图片描述
  • 三级缓存 在这里插入图片描述

在这里插入图片描述

一路返回

在这里插入图片描述

一路返回

在这里插入图片描述

一路返回

在这里插入图片描述
在这里插入图片描述
Spring源码解析-三级缓存与循环依赖

人人都能看懂的Spring源码解析,Spring如何解决循环依赖

0源码基础学习Spring源码系列——Spring如何解决循环依赖

Spring 循环依赖原理源码的探究和总结以及三级缓存的详解一万字

Spring源码分析系列-循环依赖和三级缓存

Spring源码分析系列-循环依赖和三级缓存