Spring5源码分析(4)----循环依赖原理分析

1.什么是循环依赖?

所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依 赖 A。它们之间的依赖关系如下:
在这里插入图片描述

2.一级缓存和二级缓存原理分析

2.1 一级缓存的作用

我们可以简单手写实现下创建bean的过程,如下:
重点关注getBean方法,过程已在注释中标明。

@Component
public class InstanceA  {
    @Autowired
    private InstanceB instanceB;
}

@Component
public class InstanceB  {
    @Autowired
    private InstanceA instanceA;
}
public class MainStart {
	//用于存放bean定义,key为beanName,value为beanDefinition
    private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    //一级缓存
	private static Map<String,Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 读取bean定义,并注册到beanDefinitionMap中,当然在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 {
        //1. 加载所有的BeanDefinition
        loadBeanDefinitions();
        // 2.循环bean定义,根据beanName创建Bean
        for (String beanName : beanDefinitionMap.keySet()){
            // 2.1 此时,先创建instanceA
            getBean(beanName);
        }
    }

	/**
	 * 根据bean名称创建bean
	 * @param beanName bean的名称
	 */
	private static Object getBean(String beanName) throws Exception {
		//==================实例化=========================
		//1.从bean定义容器中获取bean定义对象
		RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
		//2.从bean定义中获取到当前创建的bean的class对象
		Class<?> beanClass = beanDefinition.getBeanClass();
		//3.利用反射实例化bean
		Object instanceBean = beanClass.newInstance();
		//==================属性赋值=========================
		//4.获取到该bean所有的属性,判断有无@Autowired注解
		Field[] declaredFields = instanceBean.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			Autowired annotation = field.getAnnotation(Autowired.class);
			if(annotation != null){
				//由于属性是private,设置修改权限
				field.setAccessible(true);
				//4.1 表明该属性上有@Autowired属性,比如:
				//    @Autowired
				//    private InstanceB instanceB;
				//这时,再根据名称instanceB递归调用此方法
				Object fieldObject = getBean(field.getName()); //得到instanceB对象
				// 4.2 将创建好的instanceB bean赋值给此属性
				field.set(instanceBean,fieldObject);
			}
		}
		//==================初始化===========================
		//省略。。
		//===========将创建的bean的放入一级缓存中===============
		//5.放入一级缓存
		singletonObjects.put(beanName,instanceBean);
		return instanceBean;
	}
}

我们运行上面的main方法,会发现出现栈内存溢出。主要是出现了死循环导致的。
在这里插入图片描述
我们分析下这个过程:
当我们在创建InstanceA时,会先去实例化,然后进行属性赋值,在属性赋值的时候,发现要注入InstanceB,这时就会去创建InstanceB,即递归getBean方法;在创建InstanceB的过程中,又要对InstanceB的属性进行赋值,这时又发现要注入属性InstanceA,然后又去调用getBean去创建B,陷入死循环中。过程如下图(左侧):
在这里插入图片描述
如上图(右侧),在创建InstanceB的过程中,当进行属性赋值的时候发现有一个属性InstanceA,这时需要提供一个出口,而不是直接再去创建InstanceA。我们可以提供一个getSingleton方法,先去一级缓存中获取,如果发现缓存中有该bean,则进行返回该bean,而不需要再次去实例化、属性赋值、初始化…。除此之外,我们需要在属性赋值之前,即实例化之后,就得将bean放入一级缓存中,不然在getSingleton中获取的时候获取不到,此时一级缓存中存放的便是只完成了实例化的不完整Bean
代码修改如下:

	private static Object getBean(String beanName) throws Exception {
		//>>>>>>>>>>>添加出口代码<<<<<<<<<<<<<<
		Object singleton = getSingleton(beanName);
		if(singleton != null){
			return singleton;
		}
		//==================实例化=========================
		//1.从bean定义容器中获取bean定义对象
		RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
		//2.从bean定义中获取到当前创建的bean的class对象
		Class<?> beanClass = beanDefinition.getBeanClass();
		//3.利用反射实例化bean
		Object instanceBean = beanClass.newInstance();
		
		//>>>>>>>>>>>实例化后,将bean放入一级缓存<<<<<<<<<<<<<<
		singletonObjects.put(beanName,instanceBean);
		
		//==================属性赋值=========================
		//4.获取到该bean所有的属性,判断有无@Autowired注解
		Field[] declaredFields = instanceBean.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			Autowired annotation = field.getAnnotation(Autowired.class);
			if(annotation != null){
				field.setAccessible(true);
				//4.1 表明该属性上有@Autowired属性,比如:
				//    @Autowired
				//    private InstanceB instanceB;
				//这时,再根据名称instanceB递归调用此方法
				Object fieldObject = getBean(field.getName()); //得到instanceB对象
				// 4.2 将创建好的instanceB bean赋值给此属性
				field.set(instanceBean,fieldObject);
			}
		}
		//==================初始化===========================
		//省略。。
		//===========将创建的bean的放入一级缓存中===============
		//5.放入一级缓存
//		singletonObjects.put(beanName,instanceBean);
		return instanceBean;
	}
	
	private static Object getSingleton(String beanName){
		//1.如果一级缓存中有,则返回一级缓存中的bean
		if(singletonObjects.containsKey(beanName)){
			return singletonObjects.get(beanName);
		}
		return null;
	}

运行结果:
在这里插入图片描述

2.2 二级缓存的作用

在上面代码中,一级缓存中存放的是不完整的bean。在多线程环境下,如果在创建InstanceA的时候,另外一个线程去getBean(InstanceA),那么将会获取到一级缓存中不完整的bean,即没有完成属性赋值以及初始化的bean。所以,我们需要引入二级缓存。一级缓存还是像我们刚开始那样,存放完整的bean,二级缓存中存放不完整的bean。
流程图如下:
在这里插入图片描述
代码修改如下:

	//二级缓存,用于存放不完整的bean,将完整bean和不完整bean分离开来
	private static Map<String,Object> earlySingletonObjects = new ConcurrentHashMap<>();
	private static Object getBean(String beanName) throws Exception {
		//>>>>>>>>>>>添加出口代码<<<<<<<<<<<<<<
		Object singleton = getSingleton(beanName);
		if(singleton != null){
			return singleton;
		}
		//==================实例化=========================
		//1.从bean定义容器中获取bean定义对象
		RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
		//2.从bean定义中获取到当前创建的bean的class对象
		Class<?> beanClass = beanDefinition.getBeanClass();
		//3.利用反射实例化bean
		Object instanceBean = beanClass.newInstance();
		//修改:>>>>>>>>>>>实例化后,将bean放入二级缓存<<<<<<<<<<<<<<
		//将bean放入二级缓存中
		earlySingletonObjects.put(beanName,instanceBean);
		//==================属性赋值=========================
		//4.获取到该bean所有的属性,判断有无@Autowired注解
		Field[] declaredFields = instanceBean.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			Autowired annotation = field.getAnnotation(Autowired.class);
			if(annotation != null){
				field.setAccessible(true);
				//4.1 表明该属性上有@Autowired属性,比如:
				//    @Autowired
				//    private InstanceB instanceB;
				//这时,再根据名称instanceB递归调用此方法
				Object fieldObject = getBean(field.getName()); //得到instanceB对象
				// 4.2 将创建好的instanceB bean赋值给此属性
				field.set(instanceBean,fieldObject);
			}
		}
		//==================初始化===========================
		//省略。。
		//===========将创建的bean的放入一级缓存中===============
		//5.放入一级缓存
		singletonObjects.put(beanName,instanceBean);
		return instanceBean;
	}

	private static Object getSingleton(String beanName){
		//1.如果一级缓存中有,则返回一级缓存中的bean
		if(singletonObjects.containsKey(beanName)){
			return singletonObjects.get(beanName);
		}else if(earlySingletonObjects.containsKey(beanName)){
			//2.一级缓存中没有,则去二级缓存中获取
			return earlySingletonObjects.get(beanName);
		}
		return null;
	}

3.三级缓存原理分析

前面在二级缓存的时候,就已经解决循环依赖问题了,为什么还要引入三级缓存?1.解决循环依赖过程中的AOP增强bean情况,2.解耦,单一职责原则

3.1 二级缓存可不可以解决AOP?

我们写一个后置处理器来简单实现下:

public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		// 假设A被切点命中,需要创建代理  @PointCut("execution(* *..InstanceA.*(..))")
		if (bean instanceof InstanceA) {
			JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy();
			return jdkDynimcProxy.getProxy(bean.getClass());
		}
		return bean;
	}
}

在这里插入图片描述
在bean实例化之后,无论存不存在循环依赖,我们都在此处判断是否需要AOP增强,如果需要则返回代理后的Bean,不需要,返回之前的bean。然后,将此bean放入二级缓存中。当InstanceB需要注入InstanceA时,去二级缓存中获取即可,获取的也是AOP增强的bean。二级缓存是可以解决AOP的。

3.2 为什么还需要三级缓存?

在实例化之后就创建bean,这样是违背Spring的设计原则的。Spring结合AOP和Bean的生命周期,是在Bean创建完成之后,通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成,在这个后置处理器的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。所以,要满足两点:
1.能判断是否存在循环依赖,若存在,才会在初始化之后就创建动态代理bean
2.AOP增强需要在Bean创建完成后再去操作,如何在初始化之后,去增强实例化之后的bean呢?函数式接口进行回调。

3.2.1 判断是否存在循环依赖

添加一个Set集合,用于存放正在创建的Bean的name,我们在bean创建之前,将BeanName放入Set中,表明此bean正在创建。如果一级缓存中没有,但是Set里面有,则说明该Bean创建过程中存在循环依赖

	//循环依赖标识
	private static Set<String> singletonsCurrentlyInCreation = new HashSet<>();

在这里插入图片描述

	private static Object getSingleton(String beanName){
		//1.如果一级缓存中有,则返回一级缓存中的bean
		Object bean = singletonObjects.get(beanName);
		//2.如果一级缓存中没有,并且正在创建列表中有,则说明存在循环依赖
		if(bean == null && singletonsCurrentlyInCreation.contains(beanName)){
			//解决循环依赖。。。。。
		}
		return bean;
	}

3.2.2 三级缓存实现接口回调

	//三级缓存,value存放的不是bean对象,而是函数式接口
	private static Map<String,ObjectFactory> singletonFactories = new ConcurrentHashMap<>();

注意:三级缓存存放的是函数式接口,而不是Bean对象。引入三级缓存后,我们可以在实例化之后,放入三级缓存中。

		//3.利用反射实例化bean
		Object instanceBean = beanClass.newInstance();
		//修改:…………………………………………存入三级缓存………………………………………………………………
		singletonFactories.put(beanName, () -> new JdkProxyBeanPostProcessor().getEarlyBeanReference(instanceBean, beanName));
		//此时,不需要存入二级缓存,因为这时的bean需不要代理,无法确定,二级缓存中也不确定存什么对象
//		earlySingletonObjects.put(beanName,instanceBean);
		//==================属性赋值=========================
		//4.获取到该bean所有的属性,判断有无@Autowired注解

在getSingleton方法修改如下:
我们可以发现在此方法中,两个return返回的结果分别是二级缓存中的bean以及一级缓存中的bean。而在这个过程中,三级缓存做的就是调用回调方法进行判断是否要生成动态代理对象,以及将处理后的bean放入二级缓存中。

	private static Object getSingleton(String beanName){
		//1.如果一级缓存中有,则返回一级缓存中的bean
		Object bean = singletonObjects.get(beanName);
		//2.如果一级缓存中没有,并且正在创建列表中有,则说明存在循环依赖
		if(bean == null && singletonsCurrentlyInCreation.contains(beanName)){
			//对于下面过程5的补充
			//从二级缓存中获取
			if(earlySingletonObjects.containsKey(beanName)){
				return earlySingletonObjects.get(beanName);
			}
			//3.从三级缓存中获取
			ObjectFactory factory = singletonFactories.get(beanName);
			if(factory != null){
				//4.调用getObject方法,进行回调,处理AOP
				Object object = factory.getObject();
				//5.放入二级缓存中,避免InstanceA被依赖多次,创建多次动态代理对象的情况,所以,在三级缓存之前应加入二级缓存判断
				earlySingletonObjects.put(beanName,object);
				//6.移除三级缓存中的信息
				singletonFactories.remove(beanName);
			}
		}
		return bean;
	}

在放入一级缓存前,加入对二级缓存的判断,如果有的话要从二级缓存里面获取。

		//==================初始化===========================
		//省略。。
		//添加二级缓存的判断,从二级缓存中获取,因为有可能是动态代理的bean,而不是最初的原生bean
		if(earlySingletonObjects.containsKey(beanName)){
			instanceBean = earlySingletonObjects.get(beanName);
		}
		//===========将创建的bean的放入一级缓存中===============
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值