天天看點

Spring 循環依賴源碼分析Spring循環依賴前言一、搭積木二、問題解決三、完美解決

Spring循環依賴

文章目錄

  • Spring循環依賴
  • 前言
  • 一、搭積木
    • 1.1 定義循環依賴Bean
    • 1.2 手寫第一版本
    • 1.3 手寫第二版本
  • 二、問題解決
    • 二級緩存解決不成熟bean的情況
    • 二級緩存完美解決
    • Spring的糾結點
  • 三、完美解決

前言

循環依賴其實就是循環引用,也就是兩個或者兩個以上的bean互相持有對方,最終形成閉環。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:

Spring 循環依賴源碼分析Spring循環依賴前言一、搭積木二、問題解決三、完美解決

在本部落格,我将手寫實作一下Spring的循環依賴。試試水

一、搭積木

1.1 定義循環依賴Bean

1、student類

package com.jztai.spring.circledependence;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Teacher {

	@Autowired
	private Student student;

}

           

2、Student

package com.jztai.spring.circledependence;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Student {
	@Autowired
	private Teacher teacher;
}

           

3、動态代理

package com.jztai.spring.circledependence;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;

public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {


	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		// 簡單化,直接傳回對象
		return bean;
	}
}

           

1.2 手寫第一版本

第一版本是入門,但是會出現死循環。

package com.jztai.spring.circledependence;

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.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Written by MaShanTao
 * 第一版本的循環依賴,很明顯會出現死循環
 */
public class CircleDev1 {

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

	// 大名鼎鼎的單例緩存池
	private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/**
	 * 加載Bean定義,Spring裡面将類加載成BeanDefinition,并存在Map中
	 */
	static void loadBeanDefinations() {
		RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
		RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
		beanDefinitionMap.put("student", studentDefinition);
		beanDefinitionMap.put("teacher", teacherDefinition);
	}


	/**
	 * 擷取Bean
	 *
	 * @param beanName
	 */
	private static Object getBean(String beanName) throws Exception {
		// 1、執行個體化
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
		Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
		Class beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();
		// 2、屬性複制
		// 2.1、拿到所有的帶有Autowerid注解的屬性
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			// 拿到該字段的Autowired注解
			Autowired annotation = declaredField.getAnnotation(Autowired.class);
			// 拿到的注解不為空,就代表有該注解
			if (annotation != null) {
				// 打開該屬性的通路權限,因為通路權限是private
				declaredField.setAccessible(true);
				// 遞歸調用getBean去擷取屬性teatcher
				// Spring 可以根據Name、Type、構造函數來擷取,這裡隻寫name
				String name = declaredField.getName();
				Object bean = getBean(name);
				declaredField.set(beanInstance, bean);
			}
		}
		// 3、初始化,看看Bean有沒有實作InitializingBean接口或者有沒有指定init-method。
		// 4、添加到一級緩存
		singletonObjects.put(beanName, beanInstance);
		return beanInstance;
	}

	public static void main(String[] args) throws Exception {
		loadBeanDefinations();
		for (String beanName : beanDefinitionMap.keySet()) {
			getBean(beanName);
		}
	}

}

           
Spring 循環依賴源碼分析Spring循環依賴前言一、搭積木二、問題解決三、完美解決

1.3 手寫第二版本

1.2出現的問題是死循環,解決他的方式就是先看看單例池裡面有沒有,有的話,直接擷取單例池的即可。但是這個版本的問題是導緻Spring擷取到了不完整的bean對象。這個狀态稱之為純淨态的bean。

Spring 循環依賴源碼分析Spring循環依賴前言一、搭積木二、問題解決三、完美解決
package com.jztai.spring.circledependence;

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.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Written by MaShanTao
 * 第二版本的循環依賴,加入了判斷,如果單例池中有就直接傳回
 * 但是這個版本會導緻擷取不成熟的bean(純淨态的bean)。循環依賴的屬性得不到注入。
 */
public class CircleDev2 {

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

	// 大名鼎鼎的單例緩存池
	private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/**
	 * 加載Bean定義,Spring裡面将類加載成BeanDefinition,并存在Map中
	 */
	static void loadBeanDefinations() {
		RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
		RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
		beanDefinitionMap.put("student", studentDefinition);
		beanDefinitionMap.put("teacher", teacherDefinition);
	}


	/**
	 * 擷取Bean,先看看單例池有沒有,有的話就傳回,
	 * 但是版本一的代碼會出現單例池無法put的情況,因為循環依賴到不了初始化的那個階段,是以将put提前。
	 *
	 * @param beanName
	 */
	private static Object getBean(String beanName) throws Exception {
		// 0、看看單例池有沒有
		Object singleton = getSingleton(beanName);
		if (singleton != null) {
			return singleton;
		}
		// 1、執行個體化
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
		Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
		Class<?> beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();

		// 4、添加到一級緩存
		singletonObjects.put(beanName, beanInstance);

		// 2、屬性指派
		// 2.1、拿到所有的帶有Autowerid注解的屬性
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			// 拿到該字段的Autowired注解
			Autowired annotation = declaredField.getAnnotation(Autowired.class);
			// 拿到的注解不為空,就代表有該注解
			if (annotation != null) {
				// 打開該屬性的通路權限,因為通路權限是private
				declaredField.setAccessible(true);
				// 遞歸調用getBean去擷取屬性teatcher
				// Spring 可以根據Name、Type、構造函數來擷取,這裡隻寫name
				String name = declaredField.getName();
				Object bean = getBean(name);
				declaredField.set(beanInstance, bean);
			}
		}
		// 3、初始化,看看Bean有沒有實作InitializingBean接口或者有沒有指定init-method。
		return beanInstance;
	}

	private static Object getSingleton(String beanName) {
		if (singletonObjects.containsKey(beanName)) {
			return singletonObjects.get(beanName);
		}
		return null;
	}

	public static void main(String[] args) throws Exception {
		loadBeanDefinations();
		for (String beanName : beanDefinitionMap.keySet()) {
			getBean(beanName);
		}
		Student student = (Student) singletonObjects.get("student");
		System.out.println(student.toString());
	}

}

           

二、問題解決

二級緩存解決不成熟bean的情況

上面的版本2能實作循環依賴,但是會造成擷取到不成熟的Bean,因為A建立完成加入到一級緩存了,此時A裡面的屬性B還沒有指派呢,此時如果讀取A的話,A就是不完整的bean。是以此時二級緩存上線了。即先去一級緩存拿,如果一級緩存沒有就去二級緩存拿,總之這個緩存就是為了解決純淨bean的問題,将完整bean和純淨bean分離開來,避免讀取到不完整的bean。引入二級緩存之後其實就可以解決循環依賴的問題了。

Spring 循環依賴源碼分析Spring循環依賴前言一、搭積木二、問題解決三、完美解決
package com.jztai.spring.circledependence;

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.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Written by MaShanTao
 * 第三版本的循環依賴,引入二級緩存,解決擷取不到成熟Bean的問題。
 * 一級緩存有的話,先去一級緩存拿,如果一級緩存沒有的話,就去二級緩存拿。
 */
public class CircleDev3 {

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

	// 大名鼎鼎的單例緩存池,存放成熟Bean
	private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	// 二級緩存,為了将成熟bean和純淨bean分離開,避免讀取到不完整的bean。
	private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

	/**
	 * 加載Bean定義,Spring裡面将類加載成BeanDefinition,并存在Map中
	 */
	static void loadBeanDefinations() {
		RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
		RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
		beanDefinitionMap.put("student", studentDefinition);
		beanDefinitionMap.put("teacher", teacherDefinition);
	}


	/**
	 * 擷取Bean,先看看單例池有沒有,單例池沒有,看看二級緩存有沒有,如果有的話去二級緩存拿
	 * 一級緩存存成熟bean,二級緩存存純淨bean。
	 * 如果A在建立依賴B的時候發現二級緩存有,去拿二級緩存中的B給自己注入,注入之後A就成熟了。
	 * 此時再循環到B的時候,注入A即可,A是成熟的了,完美解決循環依賴
	 * @param beanName
	 */
	private static Object getBean(String beanName) throws Exception {
		// 0、看看單例池有沒有
		Object singleton = getSingleton(beanName);
		if (singleton != null) {
			return singleton;
		}
		// 1、執行個體化
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
		Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
		Class<?> beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();

		// 4、添加到二級緩存
		earlySingletonObjects.put(beanName, beanInstance);

		// 2、屬性指派
		// 2.1、拿到所有的帶有Autowerid注解的屬性
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			// 拿到該字段的Autowired注解
			Autowired annotation = declaredField.getAnnotation(Autowired.class);
			// 拿到的注解不為空,就代表有該注解
			if (annotation != null) {
				// 打開該屬性的通路權限,因為通路權限是private
				declaredField.setAccessible(true);
				// 遞歸調用getBean去擷取屬性teatcher
				// Spring 可以根據Name、Type、構造函數來擷取,這裡隻寫name
				String name = declaredField.getName();
				Object bean = getBean(name);
				declaredField.set(beanInstance, bean);
			}
		}
		// 3、初始化,看看Bean有沒有實作InitializingBean接口或者有沒有指定init-method。
		// 4、添加到一級緩存
		singletonObjects.put(beanName, beanInstance);
		return beanInstance;
	}

	private static Object getSingleton(String beanName) {
		if (singletonObjects.containsKey(beanName)) {
			return singletonObjects.get(beanName);
		} else if (earlySingletonObjects.containsKey(beanName)) {
			return earlySingletonObjects.get(beanName);
		}
		return null;
	}

	public static void main(String[] args) throws Exception {
		loadBeanDefinations();
		for (String beanName : beanDefinitionMap.keySet()) {
			getBean(beanName);
		}
		Student student = (Student) singletonObjects.get("student");
		System.out.println(student.toString());
	}
}

           

二級緩存完美解決

隻要在初始化Bean之後,建立動态代理,就可以完美實作Spring的循環依賴。其實從這開始要注意,二級緩存是不清理的,為的就是在循環依賴的時候有多重依賴問題,好幾個類依賴我,不能每次都從三級緩存中建立一份,是以當二級緩存沒有的時候,去三級緩存拿,拿出來之後删除三級緩存,然後加入到二級緩存,為的是多個依賴我,能從二級緩存中拿

package com.jztai.spring.circledependence;

import org.springframework.beans.factory.ObjectFactory;
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.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Written by MaShanTao
 * 第四版本的循環依賴,二級緩存完美解決循環依賴,包括Aop
 */
public class CircleDev4 {

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

	// 大名鼎鼎的單例緩存池,存放成熟Bean
	private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	// 二級緩存,為了将成熟bean和純淨bean分離開,避免讀取到不完整的bean。
	private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

	/**
	 * 加載Bean定義,Spring裡面将類加載成BeanDefinition,并存在Map中
	 */
	static void loadBeanDefinations() {
		RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
		RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
		beanDefinitionMap.put("student", studentDefinition);
		beanDefinitionMap.put("teacher", teacherDefinition);
	}


	/**
	 * 擷取Bean,先看看單例池有沒有,單例池沒有,看看二級緩存有沒有,如果有的話去二級緩存拿
	 * 一級緩存存成熟bean,二級緩存存純淨bean。
	 * 如果A在建立依賴B的時候發現二級緩存有,去拿二級緩存中的B給自己注入,注入之後A就成熟了。
	 * 此時再循環到B的時候,注入A即可,A是成熟的了,完美解決循環依賴
	 *
	 * AOP動态代理咋解決
	 * 直接在執行個體化之後,調用Bean的後置處理器,去解析Pointcut,然後建立動态代理即可。
	 * @param beanName
	 */
	private static Object getBean(String beanName) throws Exception {
		// 0、看看單例池有沒有
		Object singleton = getSingleton(beanName);
		if (singleton != null) {
			return singleton;
		}
		// 1、執行個體化
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
		Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
		Object beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();

		// 給beanInstance建立動态代理
		beanInstance = new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName);

		// 4、添加到二級緩存
		earlySingletonObjects.put(beanName, beanInstance);

		// 2、屬性指派
		// 2.1、拿到所有的帶有Autowerid注解的屬性
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			// 拿到該字段的Autowired注解
			Autowired annotation = declaredField.getAnnotation(Autowired.class);
			// 拿到的注解不為空,就代表有該注解
			if (annotation != null) {
				// 打開該屬性的通路權限,因為通路權限是private
				declaredField.setAccessible(true);
				// 遞歸調用getBean去擷取屬性teatcher
				// Spring 可以根據Name、Type、構造函數來擷取,這裡隻寫name
				String name = declaredField.getName();
				Object bean = getBean(name);
				declaredField.set(beanInstance, bean);
			}
		}
		// 3、初始化,看看Bean有沒有實作InitializingBean接口或者有沒有指定init-method。
		// 初始化之後,Spring也進行了動态代理的建立
		// 4、添加到一級緩存
		singletonObjects.put(beanName, beanInstance);
		return beanInstance;
	}

	private static Object getSingleton(String beanName) {
		if (singletonObjects.containsKey(beanName)) {
			return singletonObjects.get(beanName);
		} else if (earlySingletonObjects.containsKey(beanName)) {
			return earlySingletonObjects.get(beanName);
		}
		return null;
	}

	public static void main(String[] args) throws Exception {
		loadBeanDefinations();
		for (String beanName : beanDefinitionMap.keySet()) {
			getBean(beanName);
		}
		Student student = (Student) singletonObjects.get("student");
		System.out.println(student.toString());
	}

}
           

Spring的糾結點

其實是用二級緩存就可以完美實作Spring的循環依賴。包括動态代理,但是Spring還是希望正常的Bean(沒有循環依賴的bean)是在初始化之後建立動态代理,隻有在循環依賴的前提下才在執行個體化之後建立。 ,上面的版本,正常的bean也在執行個體化bean之後進行建立了。改一下,将執行個體化之後建立的動态代理去掉,在初始化之後建立動态代理,然後循環依賴的時候,在一級緩存中拿不到,去二級緩存中拿的時候,建立動态代理,去二級緩存拿就說明此時是處于循環依賴的。這樣就完美解決了在循環依賴的情況下,執行個體化後建立動态代理;以及沒有循環依賴的bean在初始化之後建立動态代理。

package com.jztai.spring.circledependence;

import org.springframework.beans.factory.ObjectFactory;
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.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Written by MaShanTao
 * 第五版本的循環依賴
 * Spring還是希望正常的Bean是在初始化之後建立動态代理,隻有在循環依賴的前提下才在執行個體化之後建立
 * 要判斷是否是循環依賴。二級緩存如果有的話,就代表目前處于循環依賴狀态。
 * 但是如果二級緩存有的話,getBean函數就傳回了,是以直接将建立動态代理的代碼放在getSigton函數裡面。
 * 是以其實隻用耳機緩存就可以完美實作動态代理
 */
public class CircleDev5 {

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

	// 大名鼎鼎的單例緩存池,存放成熟Bean
	private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	// 二級緩存,為了将成熟bean和純淨bean分離開,避免讀取到不完整的bean。
	private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

	// 假設Student使用了AOP,@PointCut(""),要給Student建立動态代理

	/**
	 * 加載Bean定義,Spring裡面将類加載成BeanDefinition,并存在Map中
	 */
	static void loadBeanDefinations() {
		RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
		RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
		beanDefinitionMap.put("student", studentDefinition);
		beanDefinitionMap.put("teacher", teacherDefinition);
	}


	/**
	 * 擷取Bean,先看看單例池有沒有,單例池沒有,看看二級緩存有沒有,如果有的話去二級緩存拿
	 * 一級緩存存成熟bean,二級緩存存純淨bean。
	 * 如果A在建立依賴B的時候發現二級緩存有,去拿二級緩存中的B給自己注入,注入之後A就成熟了。
	 * 此時再循環到B的時候,注入A即可,A是成熟的了,完美解決循環依賴
	 *
	 * AOP動态代理咋解決
	 * 直接在執行個體化之後,調用Bean的後置處理器,去解析Pointcut,然後建立動态代理即可。
	 *
	 * Spring還是希望正常的Bean是在初始化之後建立動态代理,隻有在循環依賴的前提下才在執行個體化之後建立
	 * 要判斷是否是循環依賴。二級緩存如果有的話,就代表目前處于循環依賴狀态。
	 * 但是如果二級緩存有的話,getBean函數就傳回了,是以直接将建立動态代理的代碼放在getSigton函數裡面。
	 * @param beanName
	 */
	private static Object getBean(String beanName) throws Exception {
		// 0、看看單例池有沒有
		Object singleton = getSingleton(beanName);
		if (singleton != null) {
			return singleton;
		}
		// 1、執行個體化
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
		Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
		Object beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();

		// 2、屬性指派
		// 2.1、拿到所有的帶有Autowerid注解的屬性
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			// 拿到該字段的Autowired注解
			Autowired annotation = declaredField.getAnnotation(Autowired.class);
			// 拿到的注解不為空,就代表有該注解
			if (annotation != null) {
				// 打開該屬性的通路權限,因為通路權限是private
				declaredField.setAccessible(true);
				// 遞歸調用getBean去擷取屬性teatcher
				// Spring 可以根據Name、Type、構造函數來擷取,這裡隻寫name
				String name = declaredField.getName();
				Object bean = getBean(name);
				declaredField.set(beanInstance, bean);
			}
		}
		// 3、初始化,看看Bean有沒有實作InitializingBean接口或者有沒有指定init-method。
		// 初始化之後,Spring也進行了動态代理的建立
		// 4、添加到一級緩存
		singletonObjects.put(beanName, beanInstance);
		return beanInstance;
	}


	private static Object getSingleton(String beanName) {
		if (singletonObjects.containsKey(beanName)) {
			return singletonObjects.get(beanName);
		} else if (earlySingletonObjects.containsKey(beanName)) {
			// Spring還是希望正常的Bean是在初始化之後建立動态代理,隻有在循環依賴的前提下才在執行個體化之後建立
			// 要判斷是否是循環依賴。二級緩存如果有的話,就代表目前處于循環依賴狀态。
			// 給beanInstance建立動态代理
			Object beanInstance = earlySingletonObjects.get(beanName);
			if(beanInstance isinstanceof JdkProxyBeanPostProcessor) return beanInstance;
			beanInstance = new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName);
			// 4、添加到二級緩存,如果有其他的也依賴于目前的對象,可以隻拿到一個單例的動态代理。
			earlySingletonObjects.put(beanName, beanInstance);
			return beanInstance;
		}
		return null;
	}

	public static void main(String[] args) throws Exception {
		loadBeanDefinations();
		for (String beanName : beanDefinitionMap.keySet()) {
			getBean(beanName);
		}
		Student student = (Student) singletonObjects.get("student");
		System.out.println(student.toString());
	}
}
           

三、完美解決

Spring 循環依賴源碼分析Spring循環依賴前言一、搭積木二、問題解決三、完美解決

其實通過二級緩存就可以實作Spring的循環依賴了。但是Spring為了解耦,在上面的方法裡面getSington方法裡面即擷取Bean又建立Bean,代碼看起來不優美,是以引入了三級緩存。引入三級緩存之後,三級緩存是工廠的緩存,裡面定義着建立動态代理的代碼;因為會出現多個依賴問題,比如A、B、C、D,四個互相依賴,那麼B再注入A的時候,給A建立了動态代理,C在注入A的時候,不能再次建立動态代理了,是以就用二級緩存來緩存動态代理對象,一級緩存還是存成熟的Bean。二級緩存的作用變了之後,沒辦法之後目前是否是循環依賴了,是以引入了Set集合來辨別目前正在建立的Bean,當再次建立時,Set集合裡面有該Bean就代表目前是循環依賴。代碼如下:

package com.jztai.spring.circledependence;

import org.springframework.beans.factory.ObjectFactory;
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;

/**
 * Written by MaShanTao
 * 第6版本的循環依賴,引入三級緩存,為了代碼解耦
 * Spring還是希望正常的Bean是在初始化之後建立動态代理,隻有在循環依賴的前提下才在執行個體化之後建立
 * 要判斷是否是循環依賴。二級緩存如果有的話,就代表目前處于循環依賴狀态。
 * 但是如果二級緩存有的話,getBean函數就傳回了,是以直接将建立動态代理的代碼放在getSigton函數裡面。
 * 是以其實隻用二級緩存就可以完美實作動态代理。
 * <p>
 * 三級緩存是Map<String, ObjectFactory<?>>,ObjectFactory就是一個函數式接口,用來存建立動态代理的執行單元。
 * 三級緩存建立代理對象之後,将代理對象放到二級緩存。二級緩存不存純淨對象了。
 * 如果A,B,C互相依賴的話,A在建立B的時候生成代理對象了,此時C再去注入B的時候不能讓他在建立一次
 * 是以就将代理對象存二級緩存,C再注入B的時候,可以直接去二級緩存拿,避免重複建立。
 * 而二級緩存不存純淨對象之後,就不能知道是否是循環依賴了。是以引入了Set辨別循環依賴
 * <p>
 * A注入的時候,去建立B,B建立的時候,利用三級緩存建立A的動态代理,并将A的動态代理注入到B,将代理對象村存到二級緩存
 * 此時B建立完之後回到A,A還是純淨對象,應該去二級緩存中拿代理對象。
 */
public class CircleDev6 {

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

	// 大名鼎鼎的單例緩存池,存放成熟Bean
	private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	// 二級緩存,為了将成熟bean和純淨bean分離開,避免讀取到不完整的bean。
	private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

	// 三級緩存,為了解決代碼耦合
	// 假設Student使用了AOP,@PointCut(""),要給Student建立動态代理
	private static final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	// 目前建立的BeanName
	private static final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	/**
	 * 加載Bean定義,Spring裡面将類加載成BeanDefinition,并存在Map中
	 */
	static void loadBeanDefinations() {
		RootBeanDefinition studentDefinition = new RootBeanDefinition(Student.class);
		RootBeanDefinition teacherDefinition = new RootBeanDefinition(Teacher.class);
		beanDefinitionMap.put("student", studentDefinition);
		beanDefinitionMap.put("teacher", teacherDefinition);
	}


	/**
	 * 擷取Bean,先看看單例池有沒有,單例池沒有,看看二級緩存有沒有,如果有的話去二級緩存拿
	 * 一級緩存存成熟bean,二級緩存存純淨bean。
	 * 如果A在建立依賴B的時候發現二級緩存有,去拿二級緩存中的B給自己注入,注入之後A就成熟了。
	 * 此時再循環到B的時候,注入A即可,A是成熟的了,完美解決循環依賴
	 * <p>
	 * AOP動态代理咋解決
	 * 直接在執行個體化之後,調用Bean的後置處理器,去解析Pointcut,然後建立動态代理即可。
	 * <p>
	 * Spring還是希望正常的Bean是在初始化之後建立動态代理,隻有在循環依賴的前提下才在執行個體化之後建立
	 * 要判斷是否是循環依賴。二級緩存如果有的話,就代表目前處于循環依賴狀态。
	 * 但是如果二級緩存有的話,getBean函數就傳回了,是以直接将建立動态代理的代碼放在getSigton函數裡面。
	 *
	 * @param beanName
	 */
	private static Object getBean(String beanName) throws Exception {
		// 0、看看單例池有沒有
		Object singleton = getSingleton(beanName);
		if (singleton != null) {
			return singleton;
		}
		if (!singletonsCurrentlyInCreation.contains(beanName)) {
			// 将目前Bean設定為正在建立。
			singletonsCurrentlyInCreation.add(beanName);
		}

		// 1、執行個體化
		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
		Class<? extends BeanDefinition> beanClass = beanDefinition.getClass();
		Object beanInstance = beanClass.getClass().getDeclaredConstructor().newInstance();

		// 循環代理的Bean才會在執行個體化這裡建立動态代理

		singletonFactories.put(beanName, () -> {
			// 動态代理根據原來的bean建立的,也就是原bean的資訊還保留着。是以下面可以直接用二級緩存中的動态代理替換執行個體。
			Object earlyBeanReference = new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName);
			return earlyBeanReference;
		});

		// 二級緩存不能寫在這裡了。因為建立代理是在三級緩存進行的,異步處理的。
		// 二級緩存應該存的是AOP對象
		// 假設A使用AOP,A在解析的時候去建立B
		// B建立的時候,執行個體化A的動态代理對象,将動态代理對象注入到B
		// A遞歸回來之後繼續建立,此時A還是純淨對象,是以應該去二級緩存拿代理對象

		earlySingletonObjects.put(beanName, beanInstance);

		// 2、屬性指派
		// 2.1、拿到所有的帶有Autowerid注解的屬性
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {
			// 拿到該字段的Autowired注解
			Autowired annotation = declaredField.getAnnotation(Autowired.class);
			// 拿到的注解不為空,就代表有該注解
			if (annotation != null) {
				// 打開該屬性的通路權限,因為通路權限是private
				declaredField.setAccessible(true);
				// 遞歸調用getBean去擷取屬性teatcher
				// Spring 可以根據Name、Type、構造函數來擷取,這裡隻寫name
				String name = declaredField.getName();
				Object bean = getBean(name);
				declaredField.set(beanInstance, bean);
			}
		}
		// 3、初始化,看看Bean有沒有實作InitializingBean接口或者有沒有指定init-method。
		// 初始化之後,Spring也進行了動态代理的建立
		// 4、添加到一級緩存
		// 如果二級緩存有的話,就代表目前Bean已經生成動态代理了,就将目前執行個體指派為二級緩存中的動态代理。
		if (earlySingletonObjects.containsKey(beanName)) {
			beanInstance = earlySingletonObjects.get(beanName);
		}
		singletonObjects.put(beanName, beanInstance);
		// 後面還要remove掉二級緩存和三級緩存,根據目前Bean是否最後一次建立啥啥的。
		return beanInstance;
	}


	// Spring為了解耦,将後置處理器代碼寫在getSingleton,擷取單例函數裡面代碼不耦合。
	private static Object getSingleton(String beanName) {
		Object bean = singletonObjects.get(beanName);
		// 一級緩存中沒有,但是目前bean辨別着正在建立,就代表着二三級緩存中有
		if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {
			// 二級緩存中存的是代理對象,先從二級緩存拿
			bean = earlySingletonObjects.get(beanName);
			if (bean == null) {
				// 一二級緩存都沒有的話,去三級緩存拿
				ObjectFactory<?> objectFactory = singletonFactories.get(beanName);
				// 三級緩存的對象工廠調用getObject函數擷取bean
				if (objectFactory != null) {
					bean = objectFactory.getObject();
					// 存到二級緩存,以防止好多個互相依賴,有多個Bean依賴A
					// 那麼A就隻用建立一次,然後放在二級緩存即可,下次直接從二級緩存中拿即可。
					earlySingletonObjects.put(beanName, bean);
					// 動态代理建立完之後就可以将目前beanName删除三級緩存
					singletonFactories.remove(beanName);
				}
			}
		}
		return bean;
	}

	public static void main(String[] args) throws Exception {
		loadBeanDefinations();
		for (String beanName : beanDefinitionMap.keySet()) {
			getBean(beanName);
		}
		Student student = (Student) singletonObjects.get("student");
		System.out.println(student.toString());
	}

}
           

Spring的實作其實和這個例子基本是一緻的。

Spring 循環依賴源碼分析Spring循環依賴前言一、搭積木二、問題解決三、完美解決