精华内容
下载资源
问答
  • 循环依赖插件 与webpack捆绑时,检测具有循环依赖性的模块。 循环依赖关系在复杂软件中通常是必需的,循环依赖关系的存在并不总是意味着存在错误,但是在您认为存在错误的情况下,此模块可能会帮助您找到它。 ...
  • 主要介绍了详解Spring Bean的循环依赖解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Spring是如何解决循环依赖的问题,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 答:Spring通过提前曝光机制,利用三级缓存解决循环依赖(这原理还是挺简单的,参考:三级缓存、图解循环依赖原理) 再问:Spring通过提前曝光,直接曝光到二级缓存已经可以解决循环依赖问题了,为什么一定要三级...
  • 主要介绍了基于SpringBoot构造器注入循环依赖及解决方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 主要介绍了详解Spring-bean的循环依赖以及解决方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了带有@Transactional和@Async的循环依赖问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 今天我们来探讨一个有意思的spring源码问题,也是一个学生告诉了我现象我从源码里面找到了这个有意思的问题。 首先我们看service层的代码案例,如下: @Service("transationServiceImpl") public class ...
  • 今天小编抽时间给大家分享spring boot启动时mybatis报循环依赖的错误,非常不错,具有参考借鉴价值,需要的朋友参考下吧
  • NULL 博文链接:https://zheng19851.iteye.com/blog/2070030
  • dagger2 中使用Lazy或Provider支持循环依赖的建议 Dagger 2.0 目前不支持任何类型的依赖循环。 Dagger 1 允许使用Lazy或Provider依赖包装器。 这里开始了一个问题: : 我已经开始使用在上述问题中发现的示例代码来...
  • 本篇文章主要介绍了浅谈Spring循环依赖的三种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了详解Spring循环依赖的解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 循环依赖完整版.mdj

    2020-09-10 08:26:42
    spring bean 循环依赖完整版,需要需要详细讲解,可以阅读博客 https://blog.csdn.net/weixin_44831431/article/details/108489287
  • 主要介绍了浅谈Spring如何解决循环依赖的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 面试必杀技,讲一讲Spring中的循环依赖

    万次阅读 多人点赞 2020-07-06 17:59:24
    为了测试循环依赖的解决情况跟注入方式的关系,我们做如下四种情况的测试 依赖情况 依赖注入方式 循环依赖是否被解决 AB相互依赖(循环依赖) 均采用setter方法注入 是 AB相互依赖(循环依赖) 均采用构造器注入 否...

    本系列文章:

    听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译

    读源码,我们可以从第一行读起

    你知道Spring是怎么解析配置类的吗?

    配置类为什么要添加@Configuration注解?

    谈谈Spring中的对象跟Bean,你知道Spring怎么创建对象的吗?

    这篇文章,我们来谈一谈Spring中的属性注入

    Spring中AOP相关的API及源码解析,原来AOP是这样子的

    你知道Spring是怎么将AOP应用到Bean的生命周期中的吗?

    推荐阅读:

    Spring官网阅读 | 总结篇

    Spring杂谈

    本系列文章将会带你一行行的将Spring的源码吃透,推荐阅读的文章是阅读源码的基础!

    前言

    Spring中的循环依赖一直是Spring中一个很重要的话题,一方面是因为源码中为了解决循环依赖做了很多处理,另外一方面是因为面试的时候,如果问到Spring中比较高阶的问题,那么循环依赖必定逃不掉。如果你回答得好,那么这就是你的必杀技,反正,那就是面试官的必杀技,这也是取这个标题的原因,当然,本文的目的是为了让你在之后的所有面试中能多一个必杀技,专门用来绝杀面试官!

    本文的核心思想就是,

    当面试官问:

    “请讲一讲Spring中的循环依赖。”的时候,

    我们到底该怎么回答?

    主要分下面几点

    1. 什么是循环依赖?
    2. 什么情况下循环依赖可以被处理?
    3. Spring是如何解决的循环依赖?

    同时本文希望纠正几个目前业界内经常出现的几个关于循环依赖的错误的说法

    1. 只有在setter方式注入的情况下,循环依赖才能解决(
    2. 三级缓存的目的是为了提高效率(

    OK,铺垫已经做完了,接下来我们开始正文

    什么是循环依赖?

    从字面上来理解就是A依赖B的同时B也依赖了A,就像下面这样

    image-20200705175322521

    体现到代码层次就是这个样子

    @Component
    public class A {
        // A中注入了B
    	@Autowired
    	private B b;
    }
    
    @Component
    public class B {
        // B中也注入了A
    	@Autowired
    	private A a;
    }
    

    当然,这是最常见的一种循环依赖,比较特殊的还有

    // 自己依赖自己
    @Component
    public class A {
        // A中注入了A
    	@Autowired
    	private A a;
    }
    

    虽然体现形式不一样,但是实际上都是同一个问题----->循环依赖

    什么情况下循环依赖可以被处理?

    在回答这个问题之前首先要明确一点,Spring解决循环依赖是有前置条件的

    1. 出现循环依赖的Bean必须要是单例
    2. 依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)

    其中第一点应该很好理解,第二点:不能全是构造器注入是什么意思呢?我们还是用代码说话

    @Component
    public class A {
    //	@Autowired
    //	private B b;
    	public A(B b) {
    
    	}
    }
    
    
    @Component
    public class B {
    
    //	@Autowired
    //	private A a;
    
    	public B(A a){
    
    	}
    }
    

    在上面的例子中,A中注入B的方式是通过构造器,B中注入A的方式也是通过构造器,这个时候循环依赖是无法被解决,如果你的项目中有两个这样相互依赖的Bean,在启动时就会报出以下错误:

    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
    

    为了测试循环依赖的解决情况跟注入方式的关系,我们做如下四种情况的测试

    依赖情况依赖注入方式循环依赖是否被解决
    AB相互依赖(循环依赖)均采用setter方法注入
    AB相互依赖(循环依赖)均采用构造器注入
    AB相互依赖(循环依赖)A中注入B的方式为setter方法,B中注入A的方式为构造器
    AB相互依赖(循环依赖)B中注入A的方式为setter方法,A中注入B的方式为构造器

    具体的测试代码跟简单,我就不放了。从上面的测试结果我们可以看到,不是只有在setter方法注入的情况下循环依赖才能被解决,即使存在构造器注入的场景下,循环依赖依然被可以被正常处理掉。

    那么到底是为什么呢?Spring到底是怎么处理的循环依赖呢?不要急,我们接着往下看

    Spring是如何解决的循环依赖?

    关于循环依赖的解决方式应该要分两种情况来讨论

    1. 简单的循环依赖(没有AOP)
    2. 结合了AOP的循环依赖

    简单的循环依赖(没有AOP)

    我们先来分析一个最简单的例子,就是上面提到的那个demo

    @Component
    public class A {
        // A中注入了B
    	@Autowired
    	private B b;
    }
    
    @Component
    public class B {
        // B中也注入了A
    	@Autowired
    	private A a;
    }
    

    通过上文我们已经知道了这种情况下的循环依赖是能够被解决的,那么具体的流程是什么呢?我们一步步分析

    首先,我们要知道Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建A

    与此同时,我们应该知道,Spring在创建Bean的过程中分为三步

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

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

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

    这些方法在之前源码分析的文章中都做过详细的解读了,如果你之前没看过我的文章,那么你只需要知道

    1. 实例化,简单理解就是new了一个对象
    2. 属性注入,为实例化中new出来的对象填充属性
    3. 初始化,执行aware接口中的方法,初始化方法,完成AOP代理

    基于上面的知识,我们开始解读整个循环依赖处理的过程,整个流程应该是以A的创建为起点,前文也说了,第一步就是创建A嘛!

    image-20200706092738559

    创建A的过程实际上就是调用getBean方法,这个方法有两层含义

    1. 创建一个新的Bean
    2. 从缓存中获取到已经被创建的对象

    我们现在分析的是第一层含义,因为这个时候缓存中还没有A嘛!

    调用getSingleton(beanName)

    首先调用getSingleton(a)方法,这个方法又会调用getSingleton(beanName, true),在上图中我省略了这一步

    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }
    

    getSingleton(beanName, true)这个方法实际上就是到缓存中尝试去获取Bean,整个缓存分为三级

    1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
    2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
    3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

    因为A是第一次被创建,所以不管哪个缓存中必然都是没有的,因此会进入getSingleton的另外一个重载方法getSingleton(beanName, singletonFactory)

    调用getSingleton(beanName, singletonFactory)

    这个方法就是用来创建Bean的,其源码如下:

    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;
        }
    }
    

    上面的代码我们主要抓住一点,通过createBean方法返回的Bean最终被放到了一级缓存,也就是单例池中。

    那么到这里我们可以得出一个结论:一级缓存中存储的是已经完全创建好了的单例Bean

    调用addSingletonFactory方法

    如下图所示:

    image-20200706105535307

    在完成Bean的实例化后,属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中,对应源码如下:

    // 这里传入的参数也是一个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);
            }
        }
    }
    

    这里只是添加了一个工厂,通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象,而这个对象实际上就是通过getEarlyBeanReference这个方法创建的。那么,什么时候会去调用这个工厂的getObject方法呢?这个时候就要到创建B的流程了。

    当A完成了实例化并添加进了三级缓存后,就要开始为A进行属性注入了,在注入时发现A依赖了B,那么这个时候Spring又会去getBean(b),然后反射调用setter方法完成属性注入。

    image-20200706114501300

    因为B需要注入A,所以在创建B的时候,又会去调用getBean(a),这个时候就又回到之前的流程了,但是不同的是,之前的getBean是为了创建Bean,而此时再调用getBean不是为了创建了,而是要从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中,所以此时getBean(a)的流程就是这样子了

    image-20200706115959250

    从这里我们可以看出,注入到B中的A是通过getEarlyBeanReference方法提前暴露出去的一个对象,还不是一个完整的Bean,那么getEarlyBeanReference到底干了啥了,我们看下它的源码

    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;
    }
    

    它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator也就是说如果在不考虑AOP的情况下,上面的代码等价于:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        return exposedObject;
    }
    

    也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP的情况下三级缓存有用嘛?讲道理,真的没什么用,我直接将这个对象放到二级缓存中不是一点问题都没有吗?如果你说它提高了效率,那你告诉我提高的效率在哪?

    image-20200706124118108

    那么三级缓存到底有什么作用呢?不要急,我们先把整个流程走完,在下文结合AOP分析循环依赖的时候你就能体会到三级缓存的作用!

    到这里不知道小伙伴们会不会有疑问,B中提前注入了一个没有经过初始化的A类型对象不会有问题吗?

    答:不会

    这个时候我们需要将整个创建A这个Bean的流程走完,如下图:

    image-20200706133018669

    从上图中我们可以看到,虽然在创建B时会提前给B注入了一个还未初始化的A对象,但是在创建A的流程中一直使用的是注入到B中的A对象的引用,之后会根据这个引用对A进行初始化,所以这是没有问题的。

    结合了AOP的循环依赖

    之前我们已经说过了,在普通的循环依赖的情况下,三级缓存没有任何作用。三级缓存实际上跟Spring中的AOP相关,我们再来看一看getEarlyBeanReference的代码:

    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;
    }
    

    如果在开启AOP的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreatorgetEarlyBeanReference方法,对应的源码如下:

    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
    

    回到上面的例子,我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。image-20200706161709829

    看到这个图你可能会产生下面这些疑问

    1. 在给B注入的时候为什么要注入一个代理对象?

    答:当我们对A进行了AOP代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象

    1. 明明初始化的时候是A对象,那么Spring是在哪里将代理对象放入到容器中的呢?

    image-20200706160542584

    在完成初始化后,Spring又调用了一次getSingleton方法,这一次传入的参数又不一样了,false可以理解为禁用三级缓存,前面图中已经提到过了,在为B中注入A时已经将三级缓存中的工厂取出,并从工厂中获取到了一个对象放入到了二级缓存中,所以这里的这个getSingleton方法做的时间就是从二级缓存中获取到这个代理后的A对象。exposedObject == bean可以认为是必定成立的,除非你非要在初始化阶段的后置处理器中替换掉正常流程中的Bean,例如增加一个后置处理器:

    @Component
    public class MyPostProcessor implements BeanPostProcessor {
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		if (beanName.equals("a")) {
    			return new A();
    		}
    		return bean;
    	}
    }
    

    不过,请不要做这种骚操作,徒增烦恼!

    1. 初始化的时候是对A对象本身进行初始化,而容器中以及注入到B中的都是代理对象,这样不会有问题吗?

    答:不会,这是因为不管是cglib代理还是jdk动态代理生成的代理类,内部都持有一个目标类的引用,当调用代理对象的方法时,实际会去调用目标对象的方法,A完成初始化相当于代理对象自身也完成了初始化

    1. 三级缓存为什么要使用工厂而不是直接使用引用?换而言之,为什么需要这个三级缓存,直接通过二级缓存暴露一个引用不行吗?

    答:这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象

    我们思考一种简单的情况,就以单独创建A为例,假设AB之间现在没有依赖关系,但是A被代理了,这个时候当A完成实例化后还是会进入下面这段代码:

    // A是单例的,mbd.isSingleton()条件满足
    // allowCircularReferences:这个变量代表是否允许循环依赖,默认是开启的,条件也满足
    // isSingletonCurrentlyInCreation:正在在创建A,也满足
    // 所以earlySingletonExposure=true
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                      isSingletonCurrentlyInCreation(beanName));
    // 还是会进入到这段代码中
    if (earlySingletonExposure) {
    	// 还是会通过三级缓存提前暴露一个工厂对象
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    

    看到了吧,即使没有循环依赖,也会将其添加到三级缓存中,而且是不得不添加到三级缓存中,因为到目前为止Spring也不能确定这个Bean有没有跟别的Bean出现循环依赖。

    假设我们在这里直接使用二级缓存的话,那么意味着所有的Bean在这一步都要完成AOP代理。这样做有必要吗?

    不仅没有必要,而且违背了Spring在结合AOP跟Bean的生命周期的设计!Spring结合AOP跟Bean的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

    三级缓存真的提高了效率了吗?

    现在我们已经知道了三级缓存的真正作用,但是这个答案可能还无法说服你,所以我们再最后总结分析一波,三级缓存真的提高了效率了吗?分为两点讨论:

    1. 没有进行AOP的Bean间的循环依赖

    从上文分析可以看出,这种情况下三级缓存根本没用!所以不会存在什么提高了效率的说法

    1. 进行了AOP的Bean间的循环依赖

    就以我们上的A、B为例,其中A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程

    image-20200706171514327

    假设不使用三级缓存,直接在二级缓存中

    image-20200706172523258

    上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言,消耗的时间是一样的

    综上,不管是哪种情况,三级缓存提高了效率这种说法都是错误的!

    总结

    面试官:”Spring是如何解决的循环依赖?“

    答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

    面试官:”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“

    答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

    一道思考题

    为什么在下表中的第三种情况的循环依赖能被解决,而第四种情况不能被解决呢?

    提示:Spring在创建Bean时默认会根据自然排序进行创建,所以A会先于B进行创建

    依赖情况依赖注入方式循环依赖是否被解决
    AB相互依赖(循环依赖)均采用setter方法注入
    AB相互依赖(循环依赖)均采用构造器注入
    AB相互依赖(循环依赖)A中注入B的方式为setter方法,B中注入A的方式为构造器
    AB相互依赖(循环依赖)B中注入A的方式为setter方法,A中注入B的方式为构造器

    小伙伴们有任何问题都可以给我留言,扫描左侧二维码可以关注我的公众号哦~
    码字不易,本文要是对你有帮助的话,记得点个赞吧!

    展开全文
  • Spring循环依赖debug源码图
  • 主要介绍了SpringIOC——DI循环依赖,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 本篇文章主要介绍了Spring循环依赖的三种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 循坏依赖即循环引用,两个或多个bean相互引用,最终形成一个环。这篇文章主要介绍了Spring循环依赖的解决办法,需要的朋友可以参考下
  • 《spring源码之循环依赖和三级缓存》整理,本人水平有限,从网上找的资料整合之后做的,请辩证的看待其中内容。
  • Spring IOC原原理理补补充充说说明明(循循环环依依赖赖Bean作作用用域域等等) 这篇文章主要介绍了Spring IOC原理补充说明(循环依赖Bean作用域等)具有很好的参考价值希望对大家有 帮助一起跟随小编过来看看吧 前前言...
  • spring如何解决循环依赖问题?

    千次阅读 多人点赞 2021-03-01 15:49:35
    循环依赖其实就是循环引用,很多地方都说需要两个或则两个以上的bean互相持有对方最终形成闭环才是循环依赖,比如A依赖于B,B依赖于C,C又依赖于A。其实一个bean持有自己类型的属性也会产生循环依赖。 setter ...

    循环依赖其实就是循环引用,很多地方都说需要两个或则两个以上的bean互相持有对方最终形成闭环才是循环依赖,比如A依赖于B,B依赖于C,C又依赖于A。其实一个bean持有自己类型的属性也会产生循环依赖。

    setter singleton循环依赖

    使用

    SingleSetterBeanA依赖SingleSetterBeanB,SingleSetterBeanB依赖SingleSetterBeanA。

    @Data
    public class SingleSetterBeanA {
    	@Autowired
    	private SingleSetterBeanB singleSetterBeanB;
    }
    
    @Data
    public class SingleSetterBeanB {
    	@Autowired
    	private SingleSetterBeanA singleSetterBeanA;
    }
    

    源码分析

    spring是通过三级缓存来解决循环依赖的,那么三级缓存是怎么工作的呢?

    三级缓存对应org.springframework.beans.factory.support.DefaultSingletonBeanRegistry类的三个属性:

    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 一级缓存
    
    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 二级缓存
    
    /** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); // 三级缓存
    

    在这里插入图片描述

    对于setter注入造成的依赖是通过Spring容器提前暴露刚完成实例化但未完成初始化的bean来完成的,而且只能解决单例作用域的bean循环依赖。通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean,关键源码如下所示:

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

    // 处理循环依赖,实例化后放入三级缓存
    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");
    	}
    	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    

    bean实例化后放入三级缓存中:

    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory

    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);
    		}
    	}
    }
    

    放入三级缓存中的是ObjectFactory类型的lambda表达式:

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference

    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;
    				/**
    				 * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference(java.lang.Object, java.lang.String)
    				 */
    				// 使用AbstractAutoProxyCreator#getEarlyBeanReference创建代理对象
    				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
    			}
    		}
    	}
    	return exposedObject;
    }
    

    构造器参数循环依赖

    通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。

    使用

    @Data
    public class SingleConstrutorBeanA {
    	public SingleConstrutorBeanA(SingleConstrutorBeanB singleConstrutorBeanB) {
    	}
    }
    
    @Data
    public class SingleConstrutorBeanB {
    	public SingleConstrutorBeanB(SingleConstrutorBeanA singleConstrutorBeanA) {
    	}
    }
    

    上面的代码运行时会抛出如下异常:

    ... ...
    Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'singleConstrutorBeanB': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'singleConstrutorBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
    	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:805)
    	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1403)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1245)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:538)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:329)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
    	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:892)
    	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:796)
    	... 76 more
    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'singleConstrutorBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
    	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:892)
    	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:796)
    	... 90 more
    

    源码分析

    Spring容器会将每一个正在创建的Bean标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。

    protected void beforeSingletonCreation(String beanName) {
    	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
    		throw new BeanCurrentlyInCreationException(beanName);
    	}
    }
    

    在这里插入图片描述

    @Lazy打破循环依赖

    在上面的例子中只需要在SingleConstrutorBeanA或者SingleConstrutorBeanB的构造方法上面加上@Lazy注解,就会发现不会抛出异常了,这又是为什么呢?

    下面假设在SingleConstrutorBeanA的构造方法上面加了@Lazy注解,在构造B时,发现参数A时被@Lazy注解修饰时,那么就不会调用getBean来获取对象,而是创建了一个代理对象,所以不会构成真正的循环依赖,不会抛出BeanCurrentlyInCreationException异常。

    /**
     * 处理懒加载对象
     * 懒加载返回的又是一个代理对象,不会真正的调用getBean,所以如果构造方法依赖中有循环依赖,那么不会报错
     * @see org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String)
     */
    Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
    		descriptor, requestingBeanName);
    if (result == null) {
    	// 调用beanFactory.getBean(beanName)从容器中获取依赖对象
    	result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    }
    return result;
    

    在这里插入图片描述

    setter prototype循环依赖

    对于prototype作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存"prototype"作用域的bean,因此无法提前暴露一个创建中的bean。

    使用

    @Data
    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PrototypeBeanA {
    	@Autowired
    	private PrototypeBeanB prototypeBeanB;
    }
    
    @Data
    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PrototypeBeanB {
    	@Autowired
    	private PrototypeBeanA prototypeBeanA;
    }
    
    @Test
    public void test3() {
    	AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    	applicationContext.register(PrototypeBeanA.class);
    	applicationContext.register(PrototypeBeanB.class);
    	applicationContext.refresh();
    	applicationContext.getBean(PrototypeBeanA.class); // 此时必须要获取Spring管理的实例,因为现在scope="prototype" 只有请求获取的时候才会实例化对象
    }
    

    运行结果如下:

    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'prototypeBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:269)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1322)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1240)
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:668)
    	... 89 more
    

    源码分析

    在这里插入图片描述

    org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    ... ...
    // 判断是否在当前创建Bean池中
    if (isPrototypeCurrentlyInCreation(beanName)) {
    	throw new BeanCurrentlyInCreationException(beanName);
    }
    ... ...
    

    异常就是在上面的代码中抛出来的,那么beanName是什么时候添加至当前创建Bean池中的呢?

    org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    else if (mbd.isPrototype()) {
    	// It's a prototype -> create a new instance.
    	// prototype类型的bean的实例化
    	Object prototypeInstance = null;
    	try {
    		beforePrototypeCreation(beanName);
    		prototypeInstance = createBean(beanName, mbd, args);
    	}
    	finally {
    		afterPrototypeCreation(beanName);
    	}
    	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }
    

    org.springframework.beans.factory.support.AbstractBeanFactory#beforePrototypeCreation

    protected void beforePrototypeCreation(String beanName) {
    	// ThreadLocal
    	Object curVal = this.prototypesCurrentlyInCreation.get();
    	if (curVal == null) {
    		this.prototypesCurrentlyInCreation.set(beanName);
    	}
    	else if (curVal instanceof String) {
    		Set<String> beanNameSet = new HashSet<>(2);
    		beanNameSet.add((String) curVal);
    		beanNameSet.add(beanName);
    		this.prototypesCurrentlyInCreation.set(beanNameSet);
    	}
    	else {
    		Set<String> beanNameSet = (Set<String>) curVal;
    		beanNameSet.add(beanName);
    	}
    }
    

    其根本原因就是Spring容器不会对prototype类型的bean进行缓存,因此无法提前利用三级缓存暴露一个代理对象。

    循环依赖开关

    可以通过allowCircularReferences来禁止循环依赖,这样的话,singleton bean的setter循环依赖也会报错。

    applicationContext.setAllowCircularReferences(false);
    

    二级缓存可行?

    缓存说明
    singletonObjects第一级缓存,存放可用的成品Bean。
    earlySingletonObjects第二级缓存,存放半成品的Bean,半成品的Bean是已创建对象,但是未注入属性和初始化,用以解决循环依赖。
    singletonFactories第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中,用以解决循环依赖。

    理论上二级缓存时可行的,只需要将三级缓存中BeanFactory创建的对象提前放入二级缓存中,这样三级缓存就可以移除了。

    那么spring中为什么还要使用三级缓存呢?如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

    展开全文
  • Spring解决循环依赖的方法

    万次阅读 多人点赞 2019-07-25 00:21:15
    从逻辑上说明Spring是怎么解决循环依赖的,然后从源码上看Spring是怎么做的。

    所谓Spring的循环依赖,指的是这样一种场景:

    当我们注入一个对象A时,需要注入对象A中标记了某些注解的属性,这些属性也就是对象A的依赖,把对象A中的依赖都初始化完成,对象A才算是创建成功。那么,如果对象A中有个属性是对象B,而且对象B中有个属性是对象A,那么对象A和对象B就算是循环依赖,如果不加处理,就会出现:创建对象A-->处理A的依赖B-->创建对象B-->处理B的对象A-->创建对象A-->处理A的依赖B-->创建对象B......这样无限的循环下去。

    这事显然不靠谱。

    Spring处理循环依赖的基本思路是这样的:

    虽说要初始化一个Bean,必须要注入Bean里的依赖,才算初始化成功,但并不要求此时依赖的依赖也都注入成功,只要依赖对象的构造方法执行完了,这个依赖对象就算存在了,注入就算成功了,至于依赖的依赖,以后再初始化也来得及(参考Java的内存模型)。

    因此,我们初始化一个Bean时,先调用Bean的构造方法,这个对象就在内存中存在了(对象里面的依赖还没有被注入),然后把这个对象保存下来,当循环依赖产生时,直接拿到之前保存的对象,于是循环依赖就被终止了,依赖注入也就顺利完成了。

    举个例子:

    假设对象A中有属性是对象B,对象B中也有属性是对象A,即A和B循环依赖。

    1. 创建对象A,调用A的构造,并把A保存下来。
    2. 然后准备注入对象A中的依赖,发现对象A依赖对象B,那么开始创建对象B。
    3. 调用B的构造,并把B保存下来。
    4. 然后准备注入B的构造,发现B依赖对象A,对象A之前已经创建了,直接获取A并把A注入B(注意此时的对象A还没有完全注入成功,对象A中的对象B还没有注入),于是B创建成功。
    5. 把创建成功的B注入A,于是A也创建成功了。

    于是循环依赖就被解决了。

    下面从Spring源码的角度看一下,具体是个什么逻辑。

    在注入一个对象的过程中,调用了这样一个方法:

    Object sharedInstance = this.getSingleton(beanName);

    这段代码在AbstractBeanFactory类的doGetBean()方法中。

    这里得到的Object就是试图是要创建的对象,beanName就是要创建的对象的类名,这里getSingleton()方法的代码如下:

    @Nullable
    protected 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<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

    这个方法是Spring解决循环依赖的关键方法,在这个方法中,使用了三层列表来查询的方式,这三层列表分别是:

    singletonObjects

    earlySingletonObjects

    singletonFactories

    这个方法中用到的几个判断逻辑,体现了Spring解决循环依赖的思路,不过实际上对象被放入这三层的顺序是和方法查询的循序相反的,也就是说,在循环依赖出现时,对象往往会先进入singletonFactories,然后earlySingletonObjects,然后singletonObjects。

    下面看一下这个方法的代码逻辑:

    1,

    Object singletonObject = this.singletonObjects.get(beanName);

    方法首先从singletonObjects中获取对象,当Spring准备新建一个对象时,singletonObjects列表中是没有这个对象的,然后进入下一步。

    2,

    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))

    除了判断null之外,有一个isSingletonCurrentlyInCreation的判断,实际上当Spring初始化了一个依赖注入的对象,但还没注入对象属性的时候,Spring会把这个bean加入singletonsCurrentlyInCreation这个set中,也就是把这个对象标记为正在创建的状态,这样,如果Spring发现要创建的bean在singletonObjects中没有,但在singletonsCurrentlyInCreation中有,基本上就可以认定为循环依赖了(在创建bean的过程中发现又要创建这个bean,说明bean的某个依赖又依赖了这个bean,即循环依赖)。

    举个例子:对象A和对象B循环依赖,那么初始化对象A之后(执行了构造方法),要把A放入singletonsCurrentlyInCreation,对象A依赖了对象B,那么就要再初始化对象B,如果这个对象B又依赖了对象A,也就是形成了循环依赖,那么当我们注入对象B中的属性A时,进入这个代码逻辑,就会发现,我们要注入的对象A已经在singletonsCurrentlyInCreation中了,后面的逻辑就该处理这种循环依赖了。

    3,

    singletonObject = this.earlySingletonObjects.get(beanName);

    这里引入了earlySingletonObjects列表,这是个为了循环依赖而存在的列表,从名字就可以看到,是个预创建的对象列表,刚刚创建的对象在这个列表里一般也没有。

    4,

    if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);

    earlySingletonObjects也没有则从singletonFactories中获取,前面说到singletonFactories是对象保存的第一步,实际上对象初始化后,可能还没有注入对象的依赖,就把对象放入了这个列表。

    如果是循环依赖,此时的singletonFactories中一般是会存在目标对象的,举个例子:对象A和对象B循环依赖,那么初始化了对象A(执行了构造方法),还没有注入对象A的依赖时,就会把A放入singletonFactories,然后开始注入A的依赖,发现A依赖B,那么需要构对象B,构造过程也是执行了B的构造后就把B放到singletonFactories,然后开始注入B的依赖,发现B依赖A,在第二步中提到,此时A已经在singletonsCurrentlyInCreation列表里了,所以会进入此段代码逻辑,而且此时时对象A在singletonFactories中确实存在,因为这已经是第二次试图创建对象A了。

    5,

    if (singletonFactory != null) {
        singletonObject = singletonFactory.getObject();
        this.earlySingletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
    }

    代码到这里基本已经确定我们要创建的这个对象已经发生循环依赖了,然后Spring进行了这样的操作,把这个对象加入到earlySingletonObjects中,然后把该对象从singletonFactories中删掉。

    6,其实上面5步已经执行完了该方法的代码,这里加的第6步是为了解释循环依赖的结果。在这个方法的代码之后,会把bean完整的进行初始化和依赖的注入,在完成了bean的初始化后,后面代码逻辑中会调用一个这样的方法:

    getSingleton(String beanName, ObjectFactory<?> singletonFactory)

    这个方法中有个小小的子方法addSingleton(),他的代码是这样的:

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

    这个方法处理的是已经注入完依赖的bean,把bean放入singletonObjects中,并把bean从earlySingletonObjects和singletonFactories中删除,这个方法和上面分析的方法组成了Spring处理循环依赖的逻辑。

    综上,Spring处理循环依赖的流程大概就是以下这样,假设对象A和对象B循环依赖:

    步骤操作三层列表中的内容
    1开始初始化对象A

    singletonFactories:

    earlySingletonObjects:

    singletonObjects:

    2调用A的构造,把A放入singletonFactories

    singletonFactories:A

    earlySingletonObjects:

    singletonObjects:

    3开始注入A的依赖,发现A依赖对象B

    singletonFactories:A

    earlySingletonObjects:

    singletonObjects:

    4开始初始化对象B

    singletonFactories:A,B

    earlySingletonObjects:

    singletonObjects:

    5调用B的构造,把B放入singletonFactories

    singletonFactories:A,B

    earlySingletonObjects:

    singletonObjects:

    6开始注入B的依赖,发现B依赖对象A

    singletonFactories:A,B

    earlySingletonObjects:

    singletonObjects:

    7

    开始初始化对象A,发现A在singletonFactories里有,则直接获取A,

    把A放入earlySingletonObjects,把A从singletonFactories删除

    singletonFactories:B

    earlySingletonObjects:A

    singletonObjects:

    8对象B的依赖注入完成

    singletonFactories:B

    earlySingletonObjects:A

    singletonObjects:

    9

    对象B创建完成,把B放入singletonObjects,

    把B从earlySingletonObjects和singletonFactories中删除

    singletonFactories:

    earlySingletonObjects:A

    singletonObjects:B

    10对象B注入给A,继续注入A的其他依赖,直到A注入完成

    singletonFactories:

    earlySingletonObjects:A

    singletonObjects:B

    11

    对象A创建完成,把A放入singletonObjects,

    把A从earlySingletonObjects和singletonFactories中删除

    singletonFactories:

    earlySingletonObjects: 

    singletonObjects:A,B

    12循环依赖处理结束,A和B都初始化和注入完成

    singletonFactories:

    earlySingletonObjects:

    singletonObjects:A,B

     

    以上,希望我说清楚了。

    另外,源码分析部分截取自这篇文章:

    https://blog.csdn.net/lkforce/article/details/95456154

    这篇文章是SpingBoot启动流程的源码分析。

    展开全文
  • Spring如何解决循环依赖问题

    万次阅读 多人点赞 2020-12-09 02:23:10
    1、什么是循环依赖:类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。 2、循环依赖问题在Spring中主要有三种情况: (1)通过构造方法进行依赖注入时产生的循环依赖问题。 (2)通过setter方法进行...

    一、循环依赖问题全景图

     

    二、什么是循环依赖问题?

    1、什么是循环依赖:

    类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。

    比如下图中A类依赖了B类,B类依赖了C类,而最后C类又依赖了A类,这样就形成了循环依赖问题。

    2、循环依赖问题案例分析:

    (1)演示代码:

    public class ClassA {
    	private ClassB classB;
    
    	public ClassB getClassB() {
    		return classB;
    	}
    
    	public void setClassB(ClassB classB) {
    		this.classB = classB;
    	}
    }
    public class ClassB {
    	private ClassA classA;
    
    	public ClassA getClassA() {
    		return classA;
    	}
    
    	public void setClassA(ClassA classA) {
    		this.classA = classA;
    	}
    }

    (2)配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<bean id="classA" class="ioc.cd.ClassA">
    		<property name="classB" ref="classB"></property>
    	</bean>
    	<bean id="classB" class="ioc.cd.ClassB">
    		<property name="classA" ref="classA"></property>
    	</bean>
    </beans>
    

    (3)测试代码:

    	@Test
    	public void test() throws Exception {
    		// 创建IoC容器,并进行初始化
    		String resource = "spring/spring-ioc-circular-dependency.xml";
    		ApplicationContext context = new ClassPathXmlApplicationContext(resource);
    		// 获取ClassA的实例(此时会发生循环依赖)
    		ClassA classA = (ClassA) context.getBean(ClassA.class);
    	}
    

    3、通过Spring IOC流程的源码分析循环依赖问题:

     

    三、循环依赖问题的类型

    循环依赖问题在Spring中主要有三种情况:

    • (1)通过构造方法进行依赖注入时产生的循环依赖问题。
    • (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
    • (3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

    在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。其实也很好解释:

    • 第(1)种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
    • 第(2)种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。

     

    四、如何解决循环依赖问题?

    1、Spring解决的单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过两个缓存来解决的,请看下图:

     

    五、Spring三大缓存介绍

    Spring中有三个缓存,用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储。如果调用getBean,则需要从三个缓存中依次获取指定的Bean实例。 读取顺序依次是一级缓存 ==> 二级缓存 ==> 三级缓存。

    1、一级缓存:Map<String, Object> singletonObjects:

    (1)第一级缓存的作用:

    • 用于存储单例模式下创建的Bean实例(已经创建完毕)。
    • 该缓存是对外使用的,指的就是使用Spring框架的程序员。

    (2)存储什么数据?

    • K:bean的名称
    • V:bean的实例对象(有代理对象则指的是代理对象,已经创建完毕)

    2、第二级缓存:Map<String, Object> earlySingletonObjects:

    (1)第二级缓存的作用:

    • 用于存储单例模式下创建的Bean实例(该Bean被提前暴露的引用,该Bean还在创建中)。
    • 该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
    • 为了解决第一个classA引用最终如何替换为代理对象的问题(如果有代理对象)

    3、第三级缓存:Map<String, ObjectFactory<?>> singletonFactories:

    (1)第三级缓存的作用:

    • 通过ObjectFactory对象来存储单例模式下提前暴露的Bean实例的引用(正在创建中)。
    • 该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
    • 此缓存是解决循环依赖最大的功臣

    (2)存储什么数据?

    • K:bean的名称
    • V:ObjectFactory,该对象持有提前暴露的bean的引用

    (3)为什么第三级缓存要使用ObjectFactory?

    如果仅仅是解决循环依赖问题,使用二级缓存就可以了,但是如果对象实现了AOP,那么注入到其他bean的时候,并不是最终的代理对象,而是原始的。这时就需要通过三级缓存的ObjectFactory才能提前产生最终的需要代理的对象。

    (4)什么时候将Bean的引用提前暴露给第三级缓存的ObjectFactory持有?时机就是在第一步实例化之后,第二步依赖注入之前,完成此操作。

     

    六、解决构造函数相互注入造成的循环依赖:

    前面说Spring可以自动解决单例模式下通过setter()方法进行依赖注入产生的循环依赖问题。而对于通过构造方法进行依赖注入时产生的循环依赖问题没办法自动解决,那针对这种情况,我们可以使用@Lazy注解来解决。

    也就是说,对于类A和类B都是通过构造器注入的情况,可以在A或者B的构造函数的形参上加个@Lazy注解实现延迟加载。@Lazy实现原理是,当实例化对象时,如果发现参数或者属性有@Lazy注解修饰,那么就不直接创建所依赖的对象了,而是使用动态代理创建一个代理类。

    比如,类A的创建:A a=new A(B),需要依赖对象B,发现构造函数的形参上有@Lazy注解,那么就不直接创建B了,而是使用动态代理创建了一个代理类B1,此时A跟B就不是相互依赖了,变成了A依赖一个代理类B1,B依赖A。但因为在注入依赖时,类A并没有完全的初始化完,实际上注入的是一个代理对象,只有当他首次被使用的时候才会被完全的初始化。

     

    展开全文
  • 它1.由同事抛的一个问题开始最近项目组的一个同事遇到了一个问题,问我的意见,一下子引起的我的兴趣,因为这个问题我也是第一次遇到。平时自认为对spring循环依赖问题还是比较了解的,直到遇...
  • 其他网址 Spring源码-循环依赖 - 草捏子的博客 | ChayCao Blog

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 422,551
精华内容 169,020
关键字:

循环依赖