关于对spring注入bean的顺序,以及spring如何保证事先加载依赖bean的问题

本文详细解析了Spring如何处理bean的循环依赖,特别是通过三级缓存来解决这一问题。介绍了bean的实例化和初始化的区别,并讨论了Spring为何需要三级缓存而非二级缓存。此外,文章探讨了构造器循环依赖无法解决的原因以及解决循环依赖的策略,如延迟加载。最后,文章提及代理对象在循环依赖中如何确保返回的是代理对象。
摘要由CSDN通过智能技术生成

一直对spring容器中注入依赖有一个疑问:当Abean有一个属性Bbean,即Abean对Bbean有依赖,那么容器是怎么保证Bbean在Abean之前加载呢?

在说这个问题答案之前,我先抛出一个问题,什么是bean的实例化,什么是bean的初始化,当Abean进行实例化的时候,是对于依赖的Bbean需要的是初始化还是实例化?

这个问题困扰了我很久,困扰的原因是spring的后置工厂处理器(beanPostProcesser)对bean实例化和初始化的定义让我先入为主,弄得我混淆了spring与java类的实例化和初始化的定义

这里我只说spring对bean实例化和初始化的定义,java类的实例化和初始化定义请看我的另一篇博客:java 类生命周期详解 以及初始化和实例化的区别好的,废话不多说,直接给答案:
bean实例化:是bean对象创建的过程。比如使用构造方法new对象,为对象在内存中分配空间。
bean初始化:是为对象中的属性赋值的过程。
如图:
在这里插入图片描述

然后,前面那个问题,当Abean进行实例化的时候,是对于依赖的Bbean需要的是是实例化,而不是初始化。
在这里插入图片描述
这里我们以上面的首先初始化A对象实例为例进行讲解整个过程。先说明:基于构造器的循环依赖spring是无法解决的。

首先Spring尝试通过ApplicationContext.getBean()方法获取A对象的实例,由于Spring容器中还没有A对象实例,因而其会创建一个A对象

然后发现其依赖了B对象,因而会尝试递归的通过ApplicationContext.getBean()方法获取B对象的实例

但是Spring容器中此时也没有B对象的实例,因而其还是会先创建一个B对象的实例。

读者需要注意这个时间点,此时A对象和B对象都已经创建了,并且保存在Spring容器中了,只不过A对象的属性b和B对象的属性a都还没有设置进去。(初始化)

在前面Spring创建B对象之后,Spring发现B对象依赖了属性A,因而还是会尝试递归的调用ApplicationContext.getBean()方法获取A对象的实例

因为Spring中已经有一个A对象的实例,虽然只是半成品(其属性b还未初始化),但其也还是目标bean,因而会将该A对象的实例返回。

此时,B对象的属性a就设置进去了,然后还是ApplicationContext.getBean()方法递归的返回,也就是将B对象的实例返回,此时就会将该实例设置到A对象的属性b中。

这个时候,注意A对象的属性b和B对象的属性a都已经设置了目标对象的实例了

读者朋友可能会比较疑惑的是,前面在为对象B设置属性a的时候,这个A类型属性还是个半成品。但是需要注意的是,这个A是一个引用,其本质上还是最开始就实例化的A对象。

而在上面这个递归过程的最后,Spring将获取到的B对象实例设置到了A对象的属性b中了

这里的A对象其实和前面设置到实例B中的半成品A对象是同一个对象,其引用地址是同一个,这里为A对象的b属性设置了值,其实也就是为那个半成品的a属性设置了值。

三级缓存

Spring能够轻松的解决属性的循环依赖正式用到了三级缓存,在DefaultSingletonBeanRegistry 中,有着3个map

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
   
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    private final Map<String, Object> earlySingletonObjects = new HashMap(16);
    //用来顺序存储beanName
 	private final Set<String> registeredSingletons = new LinkedHashSet(256);
 	//当bean处于创建时期,会放入此中,用来判断循环依赖
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
    private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap(16));
   ....}

1、singletonObjects 它是我们最熟悉的朋友,俗称“ 单例池 ”“ 容器 ”,缓存创建完成单例Bean的地方。

2、singletonFactories 映射创建Bean的原始工厂,注意,这个是工厂map

3、earlySingletonObjects 映射Bean的 早期 引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“ Bean ”,只是一个 Instance .

整个流程看个git图:
在这里插入图片描述
在这里插入图片描述

看源码之前,要说的是正常的获取bean是AbstractBeanFactory的getBean方法,他会调用到DefaultSingletonBeanRegistry 的getSingleton(俩个都会调用),但这只是第一步,之后还有doCreateBean方法等

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)从三个map中获取单例类
        Object sharedInstance = getSingleton(beanName);//这个getSingleton对应了DefaultSingletonBeanRegistry 第一个getSingleton方法,该方法在类里有俩个重载
        // 如果sharedInstance 不为空(第一次取自然为空,不为空则代表不是第一次取,代表有循环依赖)
        if (sharedInstance != null && args == null) {
   
            if (this.logger.isTraceEnabled()) {
   
                if (this.isSingletonCurrentlyInCreation(beanName)) {
   
                    this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
                } else {
   
                    this.logger.trace
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值