框架源码专题:Spring是如何解决循环依赖的?

 2023-09-15 阅读 24 评论 0

摘要:文章目录1.什么是循环依赖?2.解决循环依赖思路3. 使用了三级缓存还有什么问题?怎么解决的?4. 手写伪代码解决缓存依赖5. 二级缓存能否解决循环依赖,三级缓存存在的意义6. Spring中解决循环依赖源码6. 多例和构造器注入为什么无法解决循环依赖7.

文章目录

    • 1.什么是循环依赖?
    • 2.解决循环依赖思路
    • 3. 使用了三级缓存还有什么问题?怎么解决的?
    • 4. 手写伪代码解决缓存依赖
    • 5. 二级缓存能否解决循环依赖,三级缓存存在的意义
    • 6. Spring中解决循环依赖源码
    • 6. 多例和构造器注入为什么无法解决循环依赖
    • 7. 如何进行扩展
    • 8. spring在创建bean的时候,在哪里创建的动态代理?
    • 9. spring是如何检测循环依赖的


1.什么是循环依赖?

        所所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依 赖 A。它们之间的依赖关系如下:

什么是框架依赖,在这里插入图片描述
循环依赖代码如下:

public class InstanceA  {@Autowiredprivate InstanceB instanceB;  // InstanceA中依赖InstanceB
}
public class InstanceB  {@Autowiredprivate InstanceA instanceA; // InstanceB中依赖InstanceA
}

无论先创建InstanceA还是InstanceB时,都会发生循环依赖!


2.解决循环依赖思路

        为了更加理解循环依赖的解决思路,尝试通过手写伪代码代码来实现Bean的初始化过程,在这之前先分析一下一、二、三级缓存 存在的意义,以及解决了什么问题吧!

只使用一级缓存时:

python的依赖注入框架,先看一下流程图:中间的闭环就是循环依赖
在这里插入图片描述
        为了解决循环依赖导致的闭环问题,我们可以在闭环中增加一个出口,具体做法是:修改把bean放入一级缓存的时机,以前是属性赋值、初始化完成后才放进去,现修改为实例化完成后就先加入缓存中。并在获取某个对象时先去一级缓存中找一下,找到了直接返回,这样可以解决单线程的缓存依赖!如图所示

在这里插入图片描述

        但是这里又会带来一个新的问题,就是在多线程模式下,如果别的线程从一级缓存中获取到的是实例化后的类,这样明显是不可行的,因为这个类并不完整,是纯净的Bean,属性并没有真正被赋值!为了解决这个问题,二级缓存应运而生,初衷是分离完整bean和不完整的bean。
        
为什么要使用二级缓存?

        二级缓存作用是:暴露早期对象,为了将成熟bean 和 纯净bean 分离。
        还有一点关于bean的Aop动态代理的问题,我们都知道Bean的aop动态代理创建是在初始化之后,但是循环依赖的Bean如果使用了AOP。 那无法等到解决完循环依赖再创建动态代理, 因为这个时候已经注入属性。 所以如果循环依赖的Bean使用了aop. 需要提前创建aop。 此时使用二级缓存存储调动态代理的BeanPostProcessor也可以解决Aop的问题,

    if(二级缓存有说明是循环依赖?){  二级缓存 = 调用创建动态代理BeanPostProcessor返回代理bean(判断是否使用aop,没有依然返回原实例);}

        

java ssm框架、所以说二级缓存确实完全可以解决循环依赖的任何情况:包括扩展能力(因为也可以在这里调用BeanPostProcessor, 当然AOP也是基于BeanPostProcessor,虽然也当然可以解决),那要三级缓存干啥?
        

为什么要是用三级缓存?

        其实三级缓存的说法众说纷纭,我们只能这样解释: Spring的方法职责都比较单例,一个方法通常只做一件事, getBean就是获取bean 但是调用创建动态代理BeanPostProcessor 是属于create的过程中的, 如果在这里明显代码比较耦合,阅读性也不太好。 所以为了解耦、方法职责单一、方便后期维护, 将调用创建动态代BeanPostProcessor 放在createBean中是最合适不过了, 但是我们判断当前是否循环依赖还是要写在getSingleton里面啊,这怎么办:
         三级缓存 存一个函数接口,函数接口实现 创建动态代理调用BeanPostProcessor 。为了避免重复创建, 调用把返回的动态代理对象或者原实例存储在二级缓存,三个缓存完美解决解耦、扩展、性能、代码阅读性。


3. 使用了三级缓存还有什么问题?怎么解决的?

        使用了三级缓存,多线程模式下还是可能读取到不完整的bean,像这种线程安全问题,正常开发中基本遇不到,但是spring允许有这种操作,那spring怎么来解决的呢?答案肯定是加锁来解决。 spring中加了两把锁解决了线程安全问题! ,来看下Spring源码怎么解决的!

html5框架、第一把锁

@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// 第一步:我们尝试去一级缓存获取Object singletonObject = this.singletonObjects.get(beanName);// 一级缓存没有,如标记为正在创建的话,就去二级缓存中找if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {//这里加锁,阻塞其他线程,等待属性赋值完成synchronized (this.singletonObjects) {//尝试去二级缓存中获取对象singletonObject = this.earlySingletonObjects.get(beanName);// 二级缓存中也没有获取到对象if (singletonObject == null && allowEarlyReference) {//直接从三级缓存中获取 ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);//从三级缓存中获取到对象不为空if (singletonFactory != null) {singletonObject = singletonFactory.getObject();//把早期对象放置在二级缓存,this.earlySingletonObjects.put(beanName, singletonObject);//ObjectFactory 包装对象从三级缓存中删除掉this.singletonFactories.remove(beanName);}}}}return singletonObject;}

        将要从二级缓存获取对象时加锁,此时阻塞其他线程,直到当前线程把对象属性赋值完成,并放入一级缓存中后,在允许其他线程执行!

第二把锁:

	sharedInstance = getSingleton(beanName, () -> {try {//进入创建bean的逻辑return createBean(beanName, mbd, args);}

进入getSingleton创建中即可看到

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");//加锁synchronized (this.singletonObjects) {//其他线程从一级缓存获取一下成熟的beanObject singletonObject = this.singletonObjects.get(beanName);。。。。。 //省略代码

        这把锁锁的就是当前线程创建bean的过程,包括属性赋值、实例化、放入一级缓存都在锁范围内!保证了操作原子性。当前线程释放锁后,其他线程直接可以从一级缓存获取到成熟的bean!使用了两把锁就解决了线程安全问题!

PHP源码。

4. 手写伪代码解决缓存依赖

InstanceA 和 InstanceB 循环依赖

public class InstanceA  {@Autowiredprivate InstanceB instanceB;  // InstanceA中依赖InstanceB
}
public class InstanceB  {@Autowiredprivate InstanceA instanceA; // InstanceB中依赖InstanceA
}

模拟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(bean);return  jdkDynimcProxy.getProxy();}return bean;}
}

模拟spring解决循环依赖过程

public class MainStart_my {// beanDefinition集合private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();// 一级缓存  存的是完整的bean 单例池public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();//二级缓存 暴露早期对象,为了将成熟Bean 和 纯净bean 分离,避免多线程时读取不到完整的beanpublic static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();// 三级缓存public static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();// 循环依赖标识public static Set<String> singletonsCurrennlyInCreation = new HashSet<>();public static void main(String[] args) throws Exception {//先加载 InstanceA、B 的 bean定义RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);beanDefinitionMap.put("instanceA", aBeanDefinition);beanDefinitionMap.put("instanceB", bBeanDefinition);//根据bean定义创建Beanfor (String key : beanDefinitionMap.keySet()) {getBean(key);}}public static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {//递归终止条件:如果一级缓存中有,直接拿走Object singleton = getSingleton(beanName);if (!ObjectUtils.isEmpty(singleton)) {return singleton;}Object instanceBean = null;//第二把锁:锁住当前bean的创建过程synchronized (singletonObjects) {// 加入正在创建集合if (!singletonsCurrennlyInCreation.contains(beanName)) {singletonsCurrennlyInCreation.add(beanName);}//1. 根据beanName 实例化bean//必须强转成RootBeanDefinition,否则没有获取Class的方法RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);Class<?> beanClass = beanDefinition.getBeanClass();instanceBean = beanClass.newInstance();// 只在循环依赖的情况下在实例化后创建proxy   判断当前是不是循环依赖Object finalInstanceBean = instanceBean;singletonFactories.put(beanName, () -> new JdkProxyBeanPostProcessor().getEarlyBeanReference(finalInstanceBean, beanName));//2. 属性赋值Field[] declaredFields = beanClass.getDeclaredFields();for (Field declaredField : declaredFields) {Autowired annotation = declaredField.getAnnotation(Autowired.class);if (annotation != null) {declaredField.setAccessible(true);String name = declaredField.getName();Object bean = getBean(name); //A中需要注入B,递归调用,获取B的实例注入A的属性中declaredField.set(instanceBean, bean);}}// 3.初始化   init-mthod,省略....// 由于递归完后A 还是原实例,, 所以要从二级缓存中拿到proxy 。if (earlySingletonObjects.containsKey(beanName)) {instanceBean = earlySingletonObjects.get(beanName);}// 添加到一级缓存   AsingletonObjects.put(beanName, instanceBean);// remove 二级缓存和三级缓存  省略....}return instanceBean;}private static Object getSingleton(String beanName) {// 先从一级缓存中拿Object bean = singletonObjects.get(beanName);// 说明是循环依赖if (bean == null && singletonsCurrennlyInCreation.contains(beanName)) {//将要从二级缓存获取对象时加锁,此时阻塞其他线程synchronized (singletonObjects) {bean = earlySingletonObjects.get(beanName);// 如果二级缓存没有就从三级缓存中拿if (bean == null) {// 从三级缓存中拿ObjectFactory factory = singletonFactories.get(beanName);if (factory != null) {bean = factory.getObject(); // 拿到动态代理earlySingletonObjects.put(beanName, bean);}}}}return bean;}
}


5. 二级缓存能否解决循环依赖,三级缓存存在的意义

Java框架、首先说明一下一二三级缓存的意义:

三级缓存分别是:

  1. singletonObject:一级缓存,该缓存key = beanName, value = bean;这里的bean是已经创建完成的,该bean经历过实例化->属性填充->初始化以及各类的后置处理。因此,一旦需要获取bean时,我们第一时间就会寻找一级缓存

  2. earlySingletonObjects:二级缓存,该缓存key = beanName, value = bean;这里跟一级缓存的区别在于,该缓存所获取到的bean是提前曝光出来的,是还没创建完成的。也就是说获取到的bean只能确保已经进行了实例化,但是属性填充跟初始化肯定还没有做完,因此该bean还没创建完成,仅仅能作为指针提前曝光,被其他bean所引用

  3. singletonFactories三级缓存,不是用来存bean的实例,而是用来存函数接口、钩子函数的!该缓存key = beanName, value =beanFactory;在bean实例化完之后,属性填充以及初始化之前,如果允许提前曝光,spring会将实例化后的bean提前曝光,也就是把该bean转换成beanFactory并加入到三级缓存。在需要引用提前曝光对象时再通过singletonFactory.getObject()获取。

web前端整站源码,        这里抛出问题,如果我们直接将提前曝光的对象放到二级缓存earlySingletonObjects,Spring循环依赖时直接取就可以解决循环依赖了,为什么还要三级缓存singletonFactory然后再通过getObject()来获取呢?这不是多此一举?

我们回到添加三级缓存,添加SingletonFactory的地方,看看getObject()到底做了什么操作

    this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);});

        可以看到在返回getObject()时,多做了一步getEarlyBeanReference操作,这步操作是BeanPostProcess的一种,也就是给子类重写的一个后处理器,目的是用于被提前引用时进行拓展。即:曝光的时候并不调用该后置处理器,只有曝光,且被提前引用的时候才调用,确保了被提前引用这个时机触发。


6. Spring中解决循环依赖源码

        Spring的finishBeanFactoryInitialization方法会遍历bean定义池,拿到每一个bean定义,然后开始通过反射创建bean对象。循环依赖就发生在创建bean的doGetBean方法中

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {final String beanName = transformedBeanName(name);Object bean;// 1.可以看到,与伪代码一样,上来就调用getSingleton()方法// 这个方法内部做了很多事情,如果一级缓存中没有会操作 二、三级缓存!// 下文会进入方法内部分析	Object sharedInstance = getSingleton(beanName);// 2.第一次创建时,一级缓存中不存在实例,sharedInstance 为nullif (sharedInstance == null{//省略代码。。。。。。 if (mbd.isSingleton()) {// 3.如果sharedInstance == null ,则开始创建bean// 注意:这个getSingleton与上面那个不一样,这个方法内部有一个ObjectFactory对象// ObjectFactory对象作为一个钩子函数,当调用其内部的getObject()方法时,// 会回调createBean方法sharedInstance = getSingleton(beanName, () -> {try {// 调用ObjectFactory对象的getObject()方法时,// 会回调 createBean 方法return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}//省略代码。。。。。。 return (T) bean;}

我很依赖框架。如上代码,可以看到当拿到bean定义开始创建bean时,首先会调用getSingleton(beanName)方法,去一、二、三级缓存中尝试获取一下要创建的对象

  • 如果一级缓存中存在该对象,直接返回即可,由于对象是第一次创建,一级缓存单例池中肯定是没有的。
  • 如果不存在,且当前对象正在被创建,说明发生了循环依赖,则先去二级缓存中取
    • 如果二级缓存中有该对象,则直接返回
    • 如果二级缓存中没有该对象,则去三级缓存中取,三级缓存不是存的bean对象,而是存的钩子函数ObjectFactory
      • 如果钩子函数为空,说明该对象是第一次创建,返回对象为null ,执行外部的createBean方法创建对象
      • 如果钩子函数不为空,则执行钩子函数的getObject()方法,实际调用的是钩子函数的createBean方法进行创建对象
      • 然后把对象放入二级缓存,此处可以是原对象,也可以是proxy代理增强后的对象
      • 最后删除三级缓存中的钩子函数ObjectFactory
	@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// 1.如果一级缓存中存在该对象,直接返回即可Object singletonObject = this.singletonObjects.get(beanName);// 2.如果不存在,且当前对象正在被创建,说明发生了循环依赖if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 3.则先去二级缓存中取singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 4.如果二级缓存中没有该对象,则去三级缓存中取,// 三级缓存不是存的bean对象,而是存的钩子函数`ObjectFactory`ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 5.如果钩子函数不为空,则执行钩子函数的`getObject()`方法//   实际调用的是钩子函数的`createBean`方法进行创建对象singletonObject = singletonFactory.getObject();// 6.然后把对象放入二级缓存,此处可以是原对象,也可以是代理增强后的对象this.earlySingletonObjects.put(beanName, singletonObject);// 7.最后删除三级缓存中的钩子函数`ObjectFactory`!this.singletonFactories.remove(beanName);}}}}// 8.最终返回对象,可以是null,也可以是对象!return singletonObject;}

        当第一次创建对象时,一级缓存单例池中肯定是没有该对象的,所以会跳出getSingleton(beanName)方法,进入getSingleton(beanName,ObjectFactory)方法中,注意这两个方法是重载方法!其中ObjectFactory是一个函数式接口,内部只有一个getObject() 方法

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");//加锁防并发synchronized (this.singletonObjects) {//再次从一级缓存中获取对象Object singletonObject = this.singletonObjects.get(beanName);//如果一级缓存没有if (singletonObject == null) {.......//省略代码//把该对象标记为正在创建,放进正在创建的集合 singletonsCurrentlyInCreation 中beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {//调用钩子函数的 getObject 方法,singletonObject = singletonFactory.getObject();}.......//省略代码======================= 钩子函数 getObject() 的实现为createBean() ===============sharedInstance = getSingleton(beanName, () -> {try {// 钩子函数:匿名内部类的形式// singletonFactory.getObject() 其实调用的是 createBeanreturn createBean(beanName, mbd, args);}

由于调用了钩子函数ObjectFactorygetObject 方法,其实就是调用createBean,执行类的创建逻辑:

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 1.利用反射实例化if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}.......//省略代码// 2. 判断该对象是否是早期对象 且正在创建boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {// 3.如果是正在创建,把该对象放入三级缓存!// 在这里又用的是ObjectFactory的钩子函数,可以处理AOP// 如果下边属性赋值发生了循环依赖,则需要在属性赋值之前执行代理逻辑!这个代理逻辑就在三级缓存的钩子函数里// 发生循环依赖时,递归的调用doGetBean方法,由于此处对象实例已放入三级缓存,所以第二次递归即可获取到循环依赖的对象引用!addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}Object exposedObject = bean;try {// 4. 属性赋值,可能发生循环依赖// 发生循环依赖时,递归的调用doGetBean方法,上述逻辑再走一次!populateBean(beanName, mbd, instanceWrapper);// 5. 初始化回调exposedObject = initializeBean(beanName, exposedObject, mbd);}.......//省略代码if (earlySingletonExposure) {// 如果发生了循环依赖,由于此时还是原实例, 所以要从二级缓存中拿到proxy 。Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}}}.......//省略代码return exposedObject;}

如果属性赋值时发生了循环依赖,则递归调用链路如下所示:一共要经历三次doGetBean方法

在这里插入图片描述

框架源码,

6. 多例和构造器注入为什么无法解决循环依赖

spring属性依赖注入的三种方式:

  • 字段注入

    @Autowired  //平时常用的Autowired注入方式
    private UserService userService;
    
  • 构造器注入

    	@RestControllerpublic class UserController {private final UserService userService;//要声明一个构造器public UserController(UserService userService){this.userService = userService;}}
    
  • Setter 方法注入

    private UserService userService;//和字段注入差不多,只是这里是将@Autowired注解放到setter方法上
    @Autowired 
    public void setUserService(UserService userService){this.userService = userService;
    }
    

spring依赖、问题: 为什么多例Bean不能解决循环依赖?

        只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以多例的bean是没有缓存的,不会将其放到三级缓存中。

问题: 为什么Spring不能解决构造器注入的循环依赖?

         循环依赖的解决是实例化后通过三级缓存来解决的,但构造器是在实例化时调用的,此时bean还没有实例化完成。如果此时出现了循环依赖,一二三级缓存并没有Bean实例的任何相关信息,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。

         总结:spring的三级缓存解决的是实例化之后属性赋值的循环依赖,构造器被调用是在实例化之前,所以无法解决构造器的循环依赖!
在这里插入图片描述

javaweb项目源码?构造器循环依赖解决:使用@Lazy注解

@Component
//@RequiredArgsConstructor
public class Atest {private final Btest btest;public Atest(@Lazy Btest btest) {this.btest = btest;}
}


7. 如何进行扩展

        在doCreateBean方法中,实例化对象之后,会将该对象放入三级缓存中,此处的钩子函数ObjectFactory的内部调用了getEarlyBeanReference方法

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

钩子函数的 getEarlyBeanReference方法

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;//判读我们容器中是否有InstantiationAwareBeanPostProcessors类型的后置处理器if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {//获取我们所有的后置处理器for (BeanPostProcessor bp : getBeanPostProcessors()) {//判断我们的后置处理器是不是实现了SmartInstantiationAwareBeanPostProcessor接口if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {//进行强制转换SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;//挨个调用SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReferenceexposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}//上面都不成立,返回原始beanreturn exposedObject;}

        这个方法很重要 ,我们可以通过SmartInstantiationAwareBeanPostProcessor的后置处理器来修改我们早期对象的属性,但是spring内部的SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference没有做任何的处理,正符合spring开放式接口规范, 留给我们扩展

如何阅读框架源码。扩展案例

@Component
// 放入三级缓存时,为某些类加上动态代理!
public class TulingBPP implements SmartInstantiationAwareBeanPostProcessor {public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {if(beanName.equals("instanceA") || beanName.equals("instanceB")) {JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy(bean);return  jdkDynimcProxy.getProxy();}return bean;}
}


8. spring在创建bean的时候,在哪里创建的动态代理?

  • ①:如果没有循环依赖的话,在bean初始化完成后创建动态代理
  • ②:如果有循环依赖,在bean实例化之后创建!


9. spring是如何检测循环依赖的

利用循环依赖标识

/** 该集合缓存了当前正在创建bean的名称 */private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16));

在创建一个bean时,会把这个bean放入循环依赖标识的集合中,在获取bean的时候,先从一级缓存中拿,如果一级缓存中没有,且循环依赖标识的集合中有,就说明这个类是发生了循环依赖

		/*** 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName* IOC容器初始化加载单实例bean的时候第一次进来的时候 该集合中一般返回空,但是循环依赖的时候可以满足该条件*/if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {.....}

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/59755.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息