spring源码 循环依赖

spring框架两大核心:IOC和AOP

IOC(Inverse of Control)控制反转

将对象的创建权交给 Spring 容器去创建,利用了工厂模式将对象交给容器管理,只需要在spring配置文件中配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。不需要我们手动new去创建对象,大大降低了代码间的耦合度,使资源更加容易管理。

AOP(Aspect Oriented Programming)面向切面编程

通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。

循环依赖

问题描述

A有属性b,B有属性a,创建A时对b属性赋值需要有b对象,如果没有b就创建,但是创建B又需要对属性a赋值,这样就构成了循环依赖

前置知识

Spring在创建Bean的过程中分为三步

实例化 ,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法

属性注入,对应方法:AbstractAutowireCapableBeanFactorypopulateBean方法

初始化,对应方法: AbstractAutowireCapableBeanFactoryinitializeBean方法

其中AOP是在初始化阶段完成的

解决方案:三级缓存(3个Map)

核心思想是:bean进行实例化操作后,不需要进行依赖注入(属性赋值)操作就放入缓存中,当其他bean需要时直接从缓存中拿就行了

  • 一级缓存为:singletonObjects 缓存已经创建好的bean对象(完成了实例化,属性注入,初始化)。
  • 二级缓存为:earlySingletonObjects 缓存只完成了实例化,但是还未进行属性注入及初始化的对象
  • 三级缓存为:singletonFactories 提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

利用三级缓存解决循环依赖的流程(假设A对象中有属性b,B对象中有属性a)

首先创建A对象,先后查询一级,二级,三级缓存,都没有A这个bean对象,所以先进行实例化,实例化完成后存到三级缓存中,然后进行属性注入,A类只有一个类型为B的属性b,先后查询一级,二级,三级缓存,都没有B这个bean对象,先实例化,实例化后存到三级缓存中,进行属性注入,B类只有一个类型为A的属性a,查一级缓存,没有,查二级缓存,没有,查三级缓存,找到提前暴露的工厂,通过工厂创建A对象,将三级缓存中的创建A对象的工厂删除(remove),将创建好的A对象存进二级缓存,然后赋值给B类的a属性,B类进行初始化,此时已经创建完成,就赋值给A类的b属性,A类进行初始化,创建完成。

现在有这样一个经典的循环依赖(先不考虑AOP)

执行流程:

首先是创建A:

在getSingleton(beanName,true)方法中,依次查询一二三级缓存,发现都没有beanName为A的映射,就跳转到重载方法getSingleton(beanNeme, singletonFactory)中,这个方法的用途是创建bean,因为缓存中都没有,所以要新创建这个A对象。但是并不是简单调用createBean这个方法就完了,还要将创建好的bean(完成实例化,属性注入,初始化)存进一级缓存singletonObjects

来看下getSingleton(beanName,singletonFactory)方法源码:(看蓝色重点部分)

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) {

            // ....
            // 省略异常处理及日志
            // ....

            // 在单例对象创建前先做一个标记
            // 将beanName放入到singletonsCurrentlyInCreation这个集合中
            // 标志着这个单例Bean正在创建
            // 如果同一个单例Bean多次被创建,这里会抛出异常
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
                // ...
                // 省略catch异常处理
                // ...
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 添加到一级缓存singletonObjects中
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

这里的getObject()方法会调用createBean方法

真正完成这些的是doCreateBean方法

在doCreateBean方法中:

先进行实例化,实例化后对实例化后的bean对象封装成一个对象工厂,存进三级缓存singletonFactorys,然后才进行属性注入和初始化

// Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        //向容器中缓存单例模式的Bean对象,以防循环引用
        //判断是否是早期引用的bean,如果是,则允许其提前暴露引用
        // 这里判断的逻辑主要有三个∶
        //1.是否为单例
        //2 是否允许循环引用
        //3.是否是在创建中的bean
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            //这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
            //将还没完全配置好的bean存入到三级缓存中供其他bean使用(暴露引用)
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            //这里的getEarlyBeanReference()方法,并不是在此调用,而是声明好了方法
            //具体的调用在Spring的AbstractBeanFactory的doGetBean的第三行调用
            // DefaultSingletonBeanRegistry类的getSingleton方法中,调用
            // singletonFactory.getObject()(单例工厂的getObject方法返回实例对象)
        }
 
        // Initialize the bean instance.
        //Bean对象的初始化,依赖注入在此触发
        //这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
        Object exposedObject = bean;//暴露的对象
        try {
            //属性注入
            populateBean(beanName, mbd, instanceWrapper);
 
            //初始化bean,过程如下:
            //1.判断是否实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware方法,
            //如果有,则设置相关的属性
            //2.调用bean初始化前的前置(BeanPostProcessor)操作
            //3.执行初始化的方法
            //如果有InitializingBean,则调用afterPropertiesSet
            //如果有InitMethod,则调用初始方法
            //4.调用bean初始化的后置(BeanPostProcessor)操作
            
            //初始化
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }

这里将实例化后的bean添加到三级缓存singletonFactorys调用了方法:

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

这里的第二个参数是一个lambda表达式 () -> getEarlyBeanReference(beanName, mbd, bean),

这个addSingletonFactory是什么呢?

在通过三级缓存得到beanName对应的工厂后(objectFactory),调用getObject方法来得到对象,这个对象实际上就是通过getEarlyBeanReference方法创建的。

addSingletonFactory方法源码:

// 这里传入的参数也是一个lambda表达式,() -> getEarlyBeanReference(beanName, mbd, bean)
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // 添加到三级缓存中
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

addSingletonFactory方法源码:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    //如果要实现AOP
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

回到流程中

此时A这个bean已经实例化好并且存进三级缓存singletonFactorys中,下一步是属性注入,A中只有B类型的b这一个属性,和创建A时一样,还是

getBean->doGetBean->getSingleton(bean,true)->getSingleton(b,ObjectFactory)

->createBean->doCreateBean

实例化后存到三级缓存中,进行属性注入,B里面只有A类型的a这一个属性,依次查询一二三级缓存,然后在第三级缓存中查询到A,得到A的工厂,调用getObject()方法得到只进行了实例化的a,将a赋值给B,再进行初始化,此时B已经创建完成,将B赋值给A的b属性,A进行初始化,此时A也创建完成。

这里还要注意:从三级缓存中获取完之后,会将工厂得到的对象存进二级缓存earlySingletonObjects,然后再三级缓存中删除该beanName对应的映射。

考虑AOP的情况

如果启用了AOP,那么在A的创建过程中:

在实例化后进行存入三级缓存时,存的是代理后的对象

在B的创建过程中:

在属性注入时,从三级缓存中获取A得到的也是代理后的对象

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖是指两个或多个Bean之间相互依赖,形成一个循环引用的关系。在Spring容器启动的过程中,如果存在循环依赖,那么默认情况下会抛出BeanCurrentlyInCreationException异常。 下面是Spring循环依赖源码分析: 1. 当我们向Spring容器中注入一个Bean时,Spring会先检查这个Bean是否已经正在创建中(正在创建的Bean是无法注入的),如果正在创建中,则直接返回一个早期引用,否则继续创建Bean。 2. 在Bean的创建过程中,当遇到依赖注入(如@Autowired注解)时,Spring会检查要注入的Bean是否已经在创建中。如果是,则返回一个代理对象作为占位符,等待真正的Bean创建完毕后再进行注入。 3. Spring使用三级缓存来解决循环依赖问题。第一级缓存是单例池,存放已经创建好的单例Bean。第二级缓存是提前暴露的ObjectFactory,存放早期引用。第三级缓存是用于存放正在创建中的Bean的缓存,用于检测循环依赖。 4. 当检测到循环依赖时,Spring会尝试使用构造函数的方式完成循环依赖。它会先创建一个空对象,并将其放入到第三级缓存中。然后调用构造函数去创建这个Bean,此时依赖的Bean会返回一个早期引用。最后,将这个Bean加入到第一级缓存中,并开始注入依赖。 5. 当所有的Bean都创建完成后,Spring会触发后置处理器的回调方法,完成Bean的初始化。 总结:Spring循环依赖通过使用三级缓存和构造函数来解决,在Bean创建过程中动态地判断和处理循环依赖关系,确保所有的Bean都能被正确地创建和注入。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值