Spring循環依賴
文章目錄
- Spring循環依賴
- 前言
- 一、搭積木
-
- 1.1 定義循環依賴Bean
- 1.2 手寫第一版本
- 1.3 手寫第二版本
- 二、問題解決
-
- 二級緩存解決不成熟bean的情況
- 二級緩存完美解決
- Spring的糾結點
- 三、完美解決
前言
循環依賴其實就是循環引用,也就是兩個或者兩個以上的bean互相持有對方,最終形成閉環。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:
在本部落格,我将手寫實作一下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);
}
}
}
1.3 手寫第二版本
1.2出現的問題是死循環,解決他的方式就是先看看單例池裡面有沒有,有的話,直接擷取單例池的即可。但是這個版本的問題是導緻Spring擷取到了不完整的bean對象。這個狀态稱之為純淨态的bean。
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。引入二級緩存之後其實就可以解決循環依賴的問題了。
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為了解耦,在上面的方法裡面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的實作其實和這個例子基本是一緻的。