Spring之实例化Bean_循环依赖(4)

前面我们已经详细的了解了实例化Bean的全部过程,而本文则是基于前面的3篇博客的基础之上进行进一步分析:

实例化BeanDefinition:Spring之基于注解方式实例化BeanDefinition(1)_chen_yao_kerr的博客-CSDN博客

实例化的Bean:Spring之实例化Bean(2)_chen_yao_kerr的博客-CSDN博客 

@Resource和@Autowired注入Bean:Spring之实例化Bean _ @Resource和@Autowired实现原理(3)_chen_yao_kerr的博客-CSDN博客

其实,掌握了以上3篇关于实例化Bean和注入Bean的过程,循环依赖就变得简单了:下面看一下今天的测试类:

A:

package com.xiangxue.jack.CirDepend;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

@Component
public class A {

    String name;

    @Resource
    B b;

    public void typeName() {
        System.out.println(b.name);
    }

    @PostConstruct
    public void init() {
        name = "I am A";
        System.out.println("A 开始实例化name :" + name);
    }
}

 B:

package com.xiangxue.jack.CirDepend;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

@Component
public class B {

    String name;

    @Resource
    A a;

    public void typeName() {
        System.out.println(a.name);
    }

    @PostConstruct
    public void init() {
        name = "I am B";
        System.out.println("B 开始实例化name :" + name);
    }

}

测试入口类:

    package com.xiangxue.jack;
    
    import com.xiangxue.jack.bean.Dao;
    import com.xiangxue.jack.bean.MyTestBean2;
    import com.xiangxue.jack.postProcessor.Dao2;
    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    
    //@RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:spring.xml"})
    public class MyTest {
    
        @Autowired
        private ApplicationContext applicationContext;
    
       
    
        @Test
        public void myBean4() {
            //测试循环依赖
            System.out.println("=================开始我们的循环依赖=======================");
            applicationContext = new ClassPathXmlApplicationContext("spring.xml");
            System.out.println("=================开始调用 b 对象========================");
           
        }
    }

其实,整个循环依赖的流程就可以简单概括为10个步骤:

A类

B类A类
1. 我们调用getBean方法实例化A对象,完成后放入第3级缓存中,最后调用populatedBean方法进行依赖注入B对象。上一篇已经说过,此时会调用getBean方法实例化B对象的过程
2. 开始实例化B对象的过程, 实例化完成后,我们将B对象放入第3级缓存中。最后调用populatedBean方法进行依赖注入A对象。再次调用getBean方法实例化A对象
3. 开始实例化A对象。此时,我们发现在第3级缓存找找到了实例化A对象的ObjectFactory对象,因此不需要再次走全新的实例化新的A对象流程,直接通过objectFactory的getObject方法获取A对象
4. 此时,我们获取到了A的实例,将A对象实例注入到B对象中。
5. B类已经成功将A类实例注入
6.调用initializeBean方法,完成B类的初始化

7.调用addSingleton将初始化后的Bean放入1级缓存中,删除2、3级缓存。

返回

8,此时返回到A注入B对象的代码处. 我们获取到了B的实例,将B注入到A中
9. 调用initializeBean方法,完成A类的初始化

10。 调用addSingleton将初始化后的A实例放入1级缓存中,删除2、3级缓存。

此时,通过属性注入方式的循环依赖就结束了。一句话概括2个类的循环依赖过程。就是3次调用getBean方法的过程。下面看一下调用的全流程图:

 最后需要强调一下,spring是不支持通过构造方法注入形式的循环依赖的。

A类:

package com.xiangxue.jack.CirDepend;

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

import javax.annotation.PostConstruct;

@Component
public class A {

    String name;

    //@Resource
    B b;

    @Autowired
    A(B b) {
        System.out.println("测试构造方式支持循环依赖");
        this.b =b;
    }

    public void typeName() {
        System.out.println(b.name);
    }

    @PostConstruct
    public void init() {
        name = "I am A";
        System.out.println("A 开始实例化name :" + name);
    }
}

B类:

package com.xiangxue.jack.CirDepend;

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

import javax.annotation.PostConstruct;

@Component
public class B {

    String name;

    //@Resource
    A a;

    @Autowired
    B(A a){
        System.out.println("测试构造方式支持循环依赖");
        this.a = a;
    }
    public void typeName() {
        System.out.println(a.name);
    }

    @PostConstruct
    public void init() {
        name = "I am B";
        System.out.println("B 开始实例化name :" + name);
    }

}

如果想要通过构造函数的形式实现循环依赖,在实例化bean的时候,我们会直接抛异常信息:

A类

B类A类

1. 我们调用getBean方法实例化A对象,先从缓存中拿,第一次进来拿不到,所以把当前A对象的BeanName放入到singletonsCurrentlyInCreation集合中,便是此实例正在创建。

此时走有参构造函数实例化bean方法。获取到要注入的B对象

2. 开始实例化B对象的过程, 我们调用getBean方法实例化A对象,先从缓存中拿,第一次进来拿不到,所以把当前B对象的BeanName放入到singletonsCurrentlyInCreation集合中,便是此实例正在创建。此时走有参构造函数实例化bean方法。获取到要注入的A对象
3. 我们调用getBean方法实例化A对象。先从缓存中拿,此时我们在集合singletonsCurrentlyInCreation中找到了A的beanName,也就是说A的实例之前就一直在创建中了,所以直接抛异常

也就是说,我们实例化Bean完成以后,都会放入缓存中。而通过构造方法注入的形式实现循环依赖,一直就没有走到将实例化好的Bean放入缓存的那一步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值