spring源码学习笔记(三)我所理解的循环依赖以及模拟手写

spring源码学习笔记(三)我所理解的循环依赖以及模拟手写

每天多学一点点~
话不多说,这就开始吧…

1.前言

什么是循环依赖?网上很多博客都已经说的很清楚了,这里简单说一下。并谈谈自己的理解。不对之处欢迎讨论指正。
A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下:
在这里插入图片描述

2. spring中循环依赖源码

org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(java.lang.Class<?>...)
   --->org.springframework.context.support.AbstractApplicationContext#refresh
        ---> org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
        	   --->org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
        	        --->org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
        	             --->org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
        	                 --->org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)
        	                    --->org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

getSingleton(String beanName, boolean allowEarlyReference)

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		/**
		 * 第一步:我们尝试去一级缓存(单例缓存池中去获取对象,一般情况从该map中获取的对象是直接可以使用的)
		 * IOC容器初始化加载单实例bean的时候第一次进来的时候 该map中一般返回空
		 */
		Object singletonObject = this.singletonObjects.get(beanName);
		/**
		 * 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName
		 * IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖的时候可以满足该条件
		 */
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				/**
				 * 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
				 * 何为早期对象:就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象
				 * 就是早期对象
				 */
				singletonObject = this.earlySingletonObjects.get(beanName);
				/**
				 * 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
				 */
				if (singletonObject == null && allowEarlyReference) {
					/**
					 * 直接从三级缓存中获取 ObjectFactory 对象 这个对接就是用来解决循环依赖的关键所在
					 * 在ioc后期的过程中,当bean调用了构造方法的时候,把早期对象包裹成一个ObjectFactory
					 * 暴露到三级缓存中
					 */
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					//从三级缓存中获取到对象不为空
					if (singletonFactory != null) {
						/**
						 * 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
						 * 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
						 */
						singletonObject = singletonFactory.getObject();
						//把早期对象放置在二级缓存,
						this.earlySingletonObjects.put(beanName, singletonObject);
						//ObjectFactory 包装对象从三级缓存中删除掉
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

3. 如何解决循环依赖

想都不用想,三级缓存

/** 一级缓存 这个就是我们大名鼎鼎的单例缓存池 用于保存我们所有的单实例bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 二级缓存 ,用户缓存我们的key为beanName value是我们的早期对象(对象属性还没有来得及进行赋值) */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** 三级缓存 该map用户缓存 key为 beanName  value 为ObjectFactory(包装为早期对象)存的是函数接口ObjectFactory,函数式接口 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  1. 创建原始 bean 对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);

假设 beanA 先被创建,创建后的原始对象为BeanA@1234,上面代码中的 bean 变量指向就是这个对象。

  1. 暴露早期引用
    该方法用于把早期对象包装成一个ObjectFactory 暴露到三级缓存中 用于将解决循环依赖
    在这里插入图片描述
    beanA 指向的原始对象创建好后,就开始把指向原始对象的引用通过 ObjectFactory 暴露出去。getEarlyBeanReference 方法的第三个参数 bean 指向的正是 createBeanInstance 方法创建出原始 bean 对象 BeanA@1234。

  2. 解析依赖
    populateBean(beanName, mbd, instanceWrapper);
    populateBean 用于向 beanA 这个原始对象中填充属性,当它检测到 beanA 依赖于 beanB 时,会首先去实例化 beanB。
    beanB 在此方法处也会解析自己的依赖,当它检测到 beanA 这个依赖,于是调用 BeanFactroy.getBean(“beanA”) 这个方法,从容器中获取 beanA。

  3. 获取早期引用
    就是上面getSingleton(String beanName, boolean allowEarlyReference)源码逻辑。

接着上面的步骤讲:
1.populateBean 调用 BeanFactroy.getBean(“beanA”) 以获取 beanB 的依赖。
2.getBean(“beanB”) 会先调用 getSingleton(“beanA”),尝试从缓存中获取 beanA。此时由于 beanA 还没完全实例化好
3.于是 this.singletonObjects.get(“beanA”) 返回 null。
4.接着 this.earlySingletonObjects.get(“beanA”) 也返回空,因为 beanA 早期引用还没放入到这个缓存中。
5.最后调用 singletonFactory.getObject() 返回 singletonObject,此时 singletonObject != null。singletonObject 指向 BeanA@1234,也就是 createBeanInstance 创建的原始对象。此时 beanB 获取到了这个原始对象的引用,beanB 就能顺利完成实例化。beanB 完成实例化后,beanA 就能获取到 beanB 所指向的实例,beanA 随之也完成了实例化工作。由于 beanB.beanA 和 beanA 指向的是同一个对象 BeanA@1234,所以 beanB 中的 beanA 此时也处于可用状态了。

在这里插入图片描述

4. 面试常问问题总结

4.1 简单的描述一下spring是如何解决循环依赖的?

  1. 尝试创建bean singletonA,发现singletonA是singleton,且不是通过构造器注入依赖,那么先使用默认构造器创建一个A的实例,并保存对它的引用,并且将singletonA标记为“正在创建中的singleton”。然后发现singletonA依赖了singletonB,所以尝试创建singletonB。

  2. 尝试创建bean singletonB,发现singletonB是singleton,且不是通过构造器注入依赖,那么先使用默认构造器创建一个B的实例,并保存对它的引用,并且将singletonB标记为“正在创建中的singleton”。然后发现singletonB依赖了singletonA,所以尝试创建singletonA。

  3. 尝试创建singletonA,注意,这时Spring容器发现singletonA“正在创建中”,那么就不会再去创建singletonA,而是返回容器之前保存了的对singletonA的引用。(即上面代码判断–> 先从一级拿,拿不到,去二级,二级没有,去三级,调钩子方法)

  4. 容器将singletonA通过setter方法注入到singletonB,从而singletonB完成创建。

  5. 容器将singletonB通过setter方法注入到singletonA,从而singletonA完成创建。

上述步骤,最重要的是第1步和第3步。在第1步中,容器会保存对singletonA的引用,在第3步中,再返回对singletonA的引用,从而可以成功创建那些依赖了singletonA的bean(本例中是singletonB)。这样,循环依赖的环就在singletonA这个点这里被打破。


“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况: A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象,即调用钩子函数(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,长大成人,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象也蜕变完美了!一切都是这么神奇!!

那为什么 prototype 不能成为打破这个环的一个点呢?原因就在于Spring容器只会对singleton保存引用,而对于prototype,并不会对其保存引用,这就导致在第3步中并不能获得之前创建的bean(因为引用不到它)。还有就是,prototype 多例压根就不会放到缓存中。

4.2 为何需要三级缓存,一级、两级能不能实现?

按道理来讲,个人认为一级缓存都能实现,但这就好比你为何不把所有代码卸载一个类中一样。
性能低,耦合度高,扩展性差。比如,循环依赖下的动态代理都要放在一级缓存中,并且如果存在重复依赖要多次创建动态代理。而且为了变读取不到完整的bean在整个创建过程中都要上锁,代码编程串行,性能大大降低等等。

二级缓存作用:只要是为了分离成熟Bean和纯净Bean(未注入属性)的存放, 防止多线程中在Bean还未创建完成时读取到的Bean时不完整的。所以也是为了保证我们getBean是完整最终的Bean,不会出现不完整的情况。个人理解二级缓存也能解决aop代理问题。但是为了解耦,并且为了单一原则(比如aop是在 初始化之后的bpp中扩展点中做的)
如何判断是不是循环依赖?二级缓存有,就是。

三级缓存作用:利用函数式接口,将createBean的逻辑和getBean的逻辑解耦。
Bean的aop动态代理创建时在初始化之后,但是循环依赖的Bean如果使用了AOP,那就无法等到解决完循环依赖再创建动态代理, 因为这个时候已经注入属性。 所以如果循环依赖的Bean使用了aop. 需要提前创建aop。
在这里插入图片描述

4.3 为什么Spring不能解决构造器的循环依赖?

在Bean调用构造器实例化之前,一二三级缓存并没有Bean的任何相关信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。

4.4 为什么多例Bean不能解决循环依赖?

从spring源码可知,核心是利用一个map,来解决这个问题的,这个map就相当于缓存。
为什么可以这么做,因为我们的bean是单例的,而且是字段注入(setter注入)的,单例意味着只需要创建一次对象,后面就可以从缓存中取出来,字段注入,意味着我们无需调用构造方法进行注入。
如果是原型bean,那么就意味着每次都要去创建对象,无法利用缓存;
如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存。

4.5 循环依赖可以关闭吗

可以,Spring提供了这个功能,我们需要这么写:

    AnnotationConfigApplicationContext applicationContext = 
                new AnnotationConfigApplicationContext();
         // 关闭循环依赖
        applicationContext.setAllowCircularReferences(false);
        applicationContext.register(AppConfig.class);
        applicationContext.refresh();

5.模拟手写

  1. ObjectFactory类 模拟三级缓存钩子函数
@FunctionalInterface
public interface ObjectFactory<T> {

    /**
     * Return an instance (possibly shared or independent)
     * of the object managed by this factory.
     * @return the resulting instance
     * @throws BeansException in case of creation errors
     */
    T getObject() throws BeansException;

}

  1. IApi 接口
	public interface IApi {

	void say();
}

  1. 两个示例 InstanceA 和 InstanceB
@Component
//@Scope("prototype")
public class InstanceA implements IApi {

    @Autowired
    private InstanceB instanceB;

    public InstanceB getInstanceB() {
        return instanceB;
    }

    public void setInstanceB(InstanceB instanceB) {
        this.instanceB = instanceB;
    }

    public InstanceA(InstanceB instanceB) {
        this.instanceB = instanceB;
    }


    public InstanceA() {
        System.out.println("InstanceA实例化");
    }

	@Override
	public void say() {
		System.out.println("I'm A");
	}
}


@Component
public class InstanceB  {


    @Autowired
    private InstanceA instanceA;


    public InstanceA getInstanceA() {
        return instanceA;
    }


    public void setInstanceA(InstanceA instanceA) {
        this.instanceA = instanceA;
    }

    public InstanceB(InstanceA instanceA) {
        this.instanceA = instanceA;
    }


    public InstanceB() {
        System.out.println("InstanceB实例化");
    }
    public void bb() {
		System.out.println("I'm bb");
	}

}

  1. JdkProxyBeanPostProcessor 类,模拟动态代理
public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

	/**
	 * 模拟代理 (但是有个问题,最后 declaredField.set 时候类型会错) 所以这里先注释,
	 * 若实现aop,则真正会调用到SmartInstantiationAwareBeanPostProcessor 的实现类 AbstractAutoProxyCreator 的 getEarlyBeanReference
	 *
	 * @param bean     the raw bean instance
	 * @param beanName the name of the bean
	 * @return
	 * @throws BeansException
	 */
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		// 假设:A 被切点命中 需要创建代理  @PointCut("execution(* *..InstanceA.*(..))")
		if (bean instanceof InstanceA) {
			System.out.println("模拟  A 的 JdkProxyBeanPostProcessor   逻辑");
		}

		return bean;
	}
}
  1. MainStart类,模拟循环依赖
package com.zjq.circulardependencies;


import com.sun.javafx.logging.PulseLogger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2020/9/21 19:14
 * @description: TODO  模拟循环依赖           spring只能解决单例下 set的循环依赖
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
public class MainStart {

	private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

	/**
	 * 读取bean定义,当然在spring中肯定是根据配置 动态扫描注册
	 */
	public static void loadBeanDefinitions() {
		RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);
		RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);
		beanDefinitionMap.put("instanceA", aBeanDefinition);
		beanDefinitionMap.put("instanceB", bBeanDefinition);

	}

	public static void main(String[] args) throws Exception {
		// 加载了BeanDefinition
		loadBeanDefinitions();
		// 注册Bean的后置处理器
		
		// getBean("instanceA")或者getBean("instanceB");  时候一级缓存中2个都有了
		InstanceA instanceA = (InstanceA) getBean("instanceA");
		instanceA.say();
		InstanceB instanceB = (InstanceB) getBean("instanceB");
		instanceB.bb();
	}

	// 一级缓存 单例池   成熟态Bean
	public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();


	// 二级缓存: 为了将 成熟Bean和纯净Bean分离,避免读取到不完整得Bean
	// 二级缓存   纯净态Bean (存储不完整的Bean用于解决循环依赖中多线程读取一级缓存的脏数据)
	public static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

	// 三级缓存
	public static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();

	// 循环依赖标识(打上正在创建的标识)
	public static Set<String> singletonsCurrennlyInCreation = new HashSet<>();

	// 获取早期对象(模拟)
	public static Map<String, Object> beanWrapper = new ConcurrentHashMap<>();


	// 假设A 使用了Aop @PointCut("execution(* *..InstanceA.*(..))")   要给A创建动态代理
	// 获取Bean
	public static Object getBean(String beanName) throws Exception {
		Object singleton = getSingleton(beanName);
		if (singleton != null) {
			return singleton;
		}


		// createBean
		Object instanceBean;

		// 多线程下 避免读取到不完整Bean的 第一把锁  把整个创建过程包起来
		synchronized (singletonObjects) {


			// 加这个判断 也是为了 避免读取到不完整Bean的
			if (singletonObjects.containsKey(beanName)) {
				return singletonObjects.get(beanName);
			}


			// 实例化
			RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
			Class<?> beanClass = beanDefinition.getBeanClass();
			instanceBean = beanClass.newInstance();  // 通过无参构造函数


			// 标记为 正在创建
			if (!singletonsCurrennlyInCreation.contains(beanName)) {
				singletonsCurrennlyInCreation.add(beanName);
				beanWrapper.put(beanName, instanceBean);
			}


			// 放入三级缓存
			// 函数式 接口  lambada 写法 只有在调用  ObjectFactory.getObject时候才会回调这里
			singletonFactories.put(beanName, () ->
					new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanWrapper.get(beanName), beanName));
			// 函数式 接口  原始 写法
/*			singletonFactories.put(beanName, new ObjectFactory() {
				@Override
				public Object getObject() throws BeansException {
					return new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanWrapper.get(beanName), beanName));
					;
				}
			});*/


			// 添加到二级缓存
//		singletonFactories.put(beanName, instanceBean);

			// 属性赋值
			Field[] declaredFields = beanClass.getDeclaredFields();
			for (Field declaredField : declaredFields) {
				Autowired annotation = declaredField.getAnnotation(Autowired.class);
				// 说明属性上面有Autowired
				if (annotation != null) {
					declaredField.setAccessible(true);
					// 模拟 直接byname  bytype  byconstrator
					// instanceB
					String name = declaredField.getName();
					Object fieldObject = getBean(name);   // 递归 拿到B得Bean
					declaredField.set(instanceBean, fieldObject);
				}

			}


			// 初始化   init-mthod
			// 放在这里创建已经完了  B里面的A 不是proxy
			// 正常情况下会再 初始化之后创建proxy


			// 由于递归完后A 还是原实例, 所以要从二级缓存中拿到proxy 。
			if (earlySingletonObjects.containsKey(beanName)) {
				instanceBean = earlySingletonObjects.get(beanName);
			}

			// 添加到一级缓存   A
			singletonObjects.put(beanName, instanceBean);


			// remove 二级缓存和三级缓存
			earlySingletonObjects.clear();
			singletonFactories.clear();
		}


		return instanceBean;
	}


	public static Object getSingleton(String beanName) {
		// 先从一级缓存中拿
		Object bean = singletonObjects.get(beanName);

		// 说明是循环依赖
		if (bean == null && singletonsCurrennlyInCreation.contains(beanName)) {
			bean = earlySingletonObjects.get(beanName);
			// 如果二级缓存没有就从三级缓存中拿
			if (bean == null) {

				//   多线程 下 避免读取到不完整Bean的 第二把锁
				synchronized (singletonObjects) {

					// 从三级缓存中拿
					ObjectFactory singletonFactory = singletonFactories.get(beanName);
					if (singletonFactory != null) {
						// 拿到AOP动态代理  在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
						bean = singletonFactory.getObject();

						//把早期对象放置在二级缓存,
						earlySingletonObjects.put(beanName, bean);

						//ObjectFactory 包装对象从三级缓存中删除掉
						singletonFactories.remove(beanName);
					}
				}
			}

		}

		return bean;

	}

}

运行main方法打印

InstanceA实例化
InstanceB实例化
模拟  A 的 JdkProxyBeanPostProcessor   逻辑
I'm A

6.结语

世上无难事,只
怕有心人,每天积累一点点,fighting!!!
2021,加油,fighting,希望可以少些crud啦!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值