循环依赖
什么是循环依赖?
依赖:在Spring中,A类将其他的类当作属性时,就造成了A依赖其他类
定义:
现有A、B两个类,如果A类将B类作为属性(并设置@Autowired),B类也将A类作为属性(并设置@Autowired),那么就造成了循环依赖 【A依赖于B,B依赖于A】
现象:
当A、B循环依赖:
-
创建beanA,发现依赖beanB
-
创建beanB,发现依赖beanA
-
创建beanA,发现依赖beanB
-
创建beanB,发现依赖beanA
…// 循环
类型:
-
单例bean循环依赖 —— 三级缓存解决
-
原型bean循环依赖 —— 无法解决,直接报错
- 因为它没有用到缓存池,所以无法解决
-
构造器参数循环依赖 —— @Lazy注解解决
解决:
只有setter注入属性的方式可以解决,构造方法注入不能解决!【有可能导致构造器参数循环依赖】
关闭循环依赖
源码:
public class Main {
public static void main(String[] args) {
// 1. 执行this()
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 禁止循环依赖
applicationContext.setAllowCircularReferences(false);
// 2. 注册配置类
applicationContext.register(AppConfig.class);
// 3. 刷新容器
applicationContext.refresh();
}
}
简单解决循环依赖
定义:
在Spring中,有三个缓存池,其实用两个缓存池就可以简单的解决循环依赖问题,下面介绍用两个缓存池怎么简单解决。
缓存池组成:
缓存池 | 作用 |
---|---|
singletonObjects 一级缓存 | 存放经历了 完整生命周期的bean |
earlySingletonObjects 二级缓存 | 存放 刚创建出来的、不完整的bean 【无属性】 |
singletonFactories 三级缓存 | 存放 bean对应的函数 这里的函数支持调用BeanPostProcessor实现AOP动态代理 |
Spring解决循环依赖流程
开始
-
A进入getBean()方法
-
A开始createBean()
-
实例化纯净的BeanA
-
从三级缓存中拿A自己的回调方法 —— 出口
-
如果拿不到,将A的回调方法加入三级缓存
-
填充属性
-
填充属性发现依赖B,跑去创建beanB
-
B进入getBean()方法
-
B开始createBean()
-
实例化纯净的BeanB
-
从三级缓存中拿B自己的回调方法
-
如果拿不到,将B的回调方法加入三级缓存
-
填充属性
-
填充属性发现依赖A,跑去创建beanA
-
A进入getBean()方法
-
A开始createBean()
-
实例化纯净的BeanA
-
从三级缓存中拿A自己的回调方法,拿到了,调用回调
-
注意!此时是跑去了第一次A进入getBean()的时候
-
如果单例池、二级缓存没有A,并且有A的三级缓存函数,那么==将A加入二级缓存,并删除A的三级缓存==
-
调用回调,获取第3步实例化的那个纯净的BeanA
-
-
拿到BeanA了,返回给B
-
第二次A的 getBean() 退出
-
-
-
B继续填充属性 【使B完整】
-
加入单例池,并删除二级缓存和三级缓存的B
-
调用初始化方法
-
拿到beanB了,返回给A
-
-
-
A继续填充属性 【使A完整】
-
调用初始化方法
-
加入单例池,并删除二级缓存和三级缓存的A
结束
二级缓存和三级缓存分别解决循环依赖
-
简易版二级缓存
-
简易版三级缓存
A类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 循环依赖 实例A
*
* @author summer <995214902@qq.com>
* @date 2022/3/18 19:23
*/
@Component
public class InstanceA {
@Autowired
private InstanceB instanceB;
public InstanceA() {
System.out.println("InstanceA实例化");
}
}
B类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 循环依赖 实例A
*
* @author summer <995214902@qq.com>
* @date 2022/3/18 19:23
*/
@Component
public class InstanceB {
@Autowired
private InstanceA instanceA;
public InstanceB() {
System.out.println("InstanceB实例化");
}
}
简易版二级缓存解决循环依赖
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;
/**
* 二级缓存解决循环依赖
*
* @author summer <995214902@qq.com>
* @date 2022/3/18 19:23
*/
public class MainStart {
// 一级缓存
public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存: 为了将 成熟Bean和纯净Bean分离,避免读取到不完整得Bean
public static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
// 原料map
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();
// 1.开始啦!!! 循环创建Bean
for (String key : beanDefinitionMap.keySet()) {
// 调用getBean,先从A开始
getBean(key);
}
InstanceA singleBeanA = (InstanceA) getBean("instanceA");
}
/**
* 获取bean
*
* @param beanName bean名称
* @return bean对象
*/
public static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {
// 1. 调用getSingleton看看单例池有没有
Object singleton = getSingleton(beanName);
// 2. 单例池有直接返回,没有就直接往下走
if (singleton != null) {
return singleton;
}
// 4. 实例化一个bean
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
// 5. 拿到纯净的bean
Object instanceBean = beanClass.newInstance();
/*
* 如果是二级缓存,要实现AOP动态代理,就得在这里实现AOP动态代理
* 因为循环依赖的出口要在属性赋值之前,而这个出口就是二级缓存【第六步】
*
* 而Spring希望AOP动态代理在bean实例化并初始化完成后,再进行代理,所以引入了三级缓存。
*/
// 6. 添加二级缓存,解决循环依赖
earlySingletonObjects.put(beanName,instanceBean);
// 7. 属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field field : declaredFields) {
Autowired annotation = field.getAnnotation(Autowired.class);
// 8. 如果属性上有@Autowired,那我就给他注入
if (annotation != null) {
String name = field.getName();
// 9. 递归入口获得属性
Object fileObject = getBean(name);
field.setAccessible(true);
field.set(instanceBean, fileObject);
}
}
// 12. 调用初始化函数
// init-method
// 13. 添加到一级缓存
singletonObjects.put(beanName, instanceBean);
// 14. remove 二级缓存
earlySingletonObjects.remove(beanName);
return instanceBean;
}
public static Object getSingleton(String beanName) {
Object bean = singletonObjects.get(beanName);
if (bean == null) {
// 3. 从二级缓存中拿
bean = earlySingletonObjects.get(beanName);
}
return bean;
}
}
简易版三级缓存解决循环依赖
主流程
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.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 三级缓存解决循环依赖
*
* @author summer <995214902@qq.com>
* @date 2022/3/18 19:23
*/
public class MainStart {
// 一级缓存
public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存: 将完整bean和纯净bean分开
public static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
// 三级缓存:存储 bean 对应增强bean的方法 [AOP代理]
public static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();
// 标识正在创建的单例的set集合
public static Set<String> singletonsCurrentlyInCreation = new HashSet<>();
// 原料map
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
public static void main(String[] args) throws Exception {
// 加载了BeanDefinition
loadBeanDefinitions();
// 1.开始啦!!! 循环创建Bean
for (String key : beanDefinitionMap.keySet()) {
// 调用getBean,先从A开始
getBean(key);
}
InstanceA singleBeanA = (InstanceA) getBean("instanceA");
}
/**
* 读取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);
}
/**
* 获取bean
*
* @param beanName bean名称
* @return bean对象
*/
public static Object getBean(String beanName) throws Exception {
// 1. 调用getSingleton看看单例池有没有
Object singleton = getSingleton(beanName);
// 2. 单例池有直接返回,没有就直接往下走
if (singleton != null) {
return singleton;
}
// 3. 将bean标识为正在创建
singletonsCurrentlyInCreation.add(beanName);
// 4. 实例化一个bean
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
// 5. 拿到纯净的bean
Object instanceBean = beanClass.newInstance();
if (!singletonFactories.containsKey(beanName)) {
// 6. 三级缓存拿不到回调方法,将自己的回调方法getEarlyBeanReference() 放进去
Object finalInstanceBean = instanceBean;
singletonFactories.put(beanName, () -> getEarlyBeanReference(finalInstanceBean, beanName));
}
// 7. 属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field field : declaredFields) {
Autowired annotation = field.getAnnotation(Autowired.class);
// 8. 如果属性上有@Autowired,那我就给他注入
if (annotation != null) {
field.setAccessible(true);
String name = field.getName();
// 9. 递归入口获得属性
Object fileObject = getBean(name);
field.set(instanceBean, fileObject);
}
}
// 10. 属性赋值结束,正常情况下会在初始化之后创建proxy,就是bean有可能会被代理,所以这情况还会拿到被代理的bean
// 11. 由于递归完后,第一次的A还是原实例,所以要从二级缓存中拿到proxy
if (earlySingletonObjects.containsKey(beanName)) {
instanceBean = earlySingletonObjects.get(beanName);
}
// 12. 调用初始化函数
// init-method
// 13. 添加到一级缓存
singletonObjects.put(beanName, instanceBean);
// 14. remove 二级缓存和三级缓存
earlySingletonObjects.remove(beanName);
singletonFactories.remove(beanName);
return instanceBean;
}
/**
* 获取bean早期引用,Spring在这里进行BeanPostProcessor的调用 来实现AOP
*
* @param beanName bean名称
* @return bean对象
*/
private static Object getEarlyBeanReference(Object finalInstanceBean, String beanName) {
// 看beanName符合什么规范,让 BeanPostProcessor 给他创建动态代理 【开挂】
return finalInstanceBean;
}
public static Object getSingleton(String beanName) {
// 1. 先从一级缓存中拿
Object bean = singletonObjects.get(beanName);
// 2. 如果当前bean拿不到,只有两种情况:
// 一、刚好是一个新的类刚刚开始创建 二、我之前已经正在创建了,又要get一次,说明是循环依赖
if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {
// 3. 从二级缓存中拿
bean = earlySingletonObjects.get(beanName);
if (bean == null) {
// 4. 从三级缓存中拿
ObjectFactory factory = singletonFactories.get(beanName);
if (factory != null) {
// 5. 拿到被动态代理的、纯净的bean
bean = factory.getObject();
// 6. 加入二级缓存,给被依赖的bean获取 【B】
earlySingletonObjects.put(beanName, bean);
}
}
}
return bean;
}
}
ObjectFactory函数接口
/**
* 循环依赖,函数接口
* <p>
* 函数接口很有意思
* <p>
* 它可以将一个函数作为参数传递,然后在你想要调用的时机调用这个函数
*
* @author summer <995214902@qq.com>
* @date 2022/3/18 19:23
*/
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* 返回此工厂管理的对象的实例,在这里可以增强bean
*
* @return 结果实例
*/
T getObject();
}
Q
为什么二级缓存就能解决循环依赖,还要三级缓存?
因为Spring希望在 bean实例化并初始化之后 再去实现AOP动态代理
- 只用两级缓存的话,AOP动态代理必须放到属性赋值之前,不然没有循环依赖出口!
好处:解耦、便于实现AOP动态代理吧~
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njvhSdau-1652175560946)(3%20%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96.assets/%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E8%AF%BE%E4%B8%8A%E5%9B%BE%E3%80%90%E6%9B%B4%E5%A4%9A%E8%AF%BE%E7%A8%8B%20zx-cc.net%E3%80%91.png)]
Spring中循环依赖解决
就是getBean()那里分析的
-
利用三级缓存存储 函数方法进行回调获取早期对象解决循环依赖
-
考虑AOP是否应该提前 【实例化后 AOP 还是 初始化后AOP】
-
实例化后 —— getEarlyReference() 里面调用BeanPostProcessor
-
初始化后 —— applyBeanPostProcessorsAfterInitialization() 里面调用BeanPostProcessor
-
就做了这两件事来解决循环依赖的问题