精华内容
下载资源
问答
  • spring循环依赖
    2022-03-28 20:05:15

    目录

    什么是循环依赖

    如何解决循环依赖

    两层可不可以?


    Spring 是如何解决循环依赖问题的_从菜鸟到放弃的博客-CSDN博客_spring 循环依赖怎么解决

    什么是循环依赖

    多个bean之间相互依赖,形成了一个闭环。 比如:A依赖于B、B依赖于c、c依赖于A

    通常来说,如果问spring容器内部如何解决循环依赖, 一定是指默认的单例Bean中,属性互相引用的场景。也就是说,Spring的循环依赖,是Spring容器注入时候出现的问题。

    比如

    @Bean
    public class A {
        @Autowire
        private B b;
    }
     
     
    @Bean
    public class B {
        @Autowire
        private A a;
    }

    如何解决循环依赖

    Spring对循环依赖的解决方法可以概括为 用三级缓存方式达到Bean提前曝光的目的

    A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A,B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A放到一级缓存中。

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

    spring内部有三级缓存:

    • singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例
    • earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例
    • singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。

     
    Spring的三级缓存_此间少年tq的博客-CSDN博客_spring三级缓存

    // 从上至下 分表代表这“三级缓存”
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
        private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存

    singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
    earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
    singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

    两层可不可以?

    二级缓存不能保证多个依赖中实例化对象的唯一性。

    如果是这种情况:TestService1依赖于TestService2和TestService3,而TestService2依赖于TestService1,同时TestService3也依赖于TestService1。

    按照上图的流程可以把TestService1注入到TestService2,并且TestService1的实例是从第三级缓存中获取的。

    假设不用第二级缓存,TestService1注入到TestService3又需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的实例对象,而是ObjectFactory对象。说白了,两次从三级缓存中获取都是ObjectFactory对象,当需被代理时通过它创建的实例对象可能会不一样的。

    为了解决这个问题,spring引入的第二级缓存。上面图1其实TestService1对象的实例已经被添加到第二级缓存中了,而在TestService1注入到TestService3时,只用从第二级缓存中获取该对象即可。
     

    更多相关内容
  • spring循环依赖

    千次阅读 2022-04-28 15:04:30
    spring循环依赖主要有三种: 单例引用类型循环依赖(属性):允许 构造器的循环依赖:不允许 多例循环依赖:不允许 单例引用类型循环依赖(属性) package com.spring.bean; import lombok.Data; import org....

    spring循环依赖主要有三种:

            单例引用类型循环依赖(属性):允许

            构造器的循环依赖:不允许

            多例循环依赖:不允许

    单例引用类型循环依赖(属性)

    package com.spring.bean;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    public class CirculeA {
        @Autowired
        private CirculeB circuleB;//引用CirculeB
    
    }
    package com.spring.bean;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    public class CirculeB {
    
        @Autowired
        private CirculeA circuleA;//引用CirculeA 
    

     @org.junit.Test
        public void test3(){
            ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
            System.out.println("spring启动成功");
        }

    总结:在circuleA实例化过程中触发circuleB的getBean(),此时circileA的实例已放入到三级缓存中,在circuleB的实例化过程中会触发circuleA的genBean(),直接从缓存中拿到circileA的实例,这样会优先将circuleB是实例化完成,并在circuleA触发circuleB的getBean()时返回,然后继续完成circuleA的实例化;

    circuleA第一次实例化会走以下代码,第二次直接从缓存中获取不会走以下代码

     

        单例实例化流程图: 

    构造器的循环依赖:  

    package com.spring.bean;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    public class CirculeB {
    
        private CirculeA circuleA;
    
        public CirculeB(CirculeA circuleA) {
            this.circuleA = circuleA;
        }
    }
    
    package com.spring.bean;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    public class CirculeA {
    
        private CirculeB circuleB;
    
        public CirculeA(CirculeB circuleB) {
            this.circuleB = circuleB;
        }
    }
    

    报错:org.springframework.beans.factory.UnsatisfiedDependencyException: 
    Error creating bean with name 'circuleA' defined in file [D:\XXX\5.2.8\spring_demo\spring_test\target\classes\com\spring\bean\CirculeA.class]: 
    Unsatisfied dependency expressed through constructor parameter 0; 
    nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
    Error creating bean with name 'circuleB' defined in file [D:\xxxx\5.2.8\spring_demo\spring_test\target\classes\com\spring\bean\CirculeB.class]:
     Unsatisfied dependency expressed through constructor parameter 0; 
     nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
     Error creating bean with name 'circuleA': Requested bean is currently in creation: Is there an unresolvable circular reference?

            //创建实例,在这个方法中触发circuleB的getBean()
    		if (instanceWrapper == null) {
    			instanceWrapper = createBeanInstance(beanName, mbd, args);
    		}
    		Object bean = instanceWrapper.getWrappedInstance();
    		Class<?> beanType = instanceWrapper.getWrappedClass();
    		if (beanType != NullBean.class) {
    			mbd.resolvedTargetType = beanType;
    		}
    
    		// Allow post-processors to modify the merged bean definition.
    		synchronized (mbd.postProcessingLock) {
    			if (!mbd.postProcessed) {
    				try {
    					//AutowiredAnnotationBeanPostProcessor 收集有@Autowire和@Value注解的方法和属性,
    					// 放入到injectionMetadataCache缓存中,包装为InjectionMetadata.InjectedElement对象,其中有member,isFiled属性相对重要
    					//CommonAnnotationBeanPostProcessor 收集有@PostConstruct和@PreDestroy注解的方法 放入到lifecycleMetadataCache,
    					// 有@Resource注解的方法和属性 放入到injectionMetadataCache缓存中,
    					// 包装为InjectionMetadata.InjectedElement对象,其中有member,isFiled属性相对重要
    					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    				}
    				catch (Throwable ex) {
    					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    							"Post-processing of merged bean definition failed", ex);
    				}
    				mbd.postProcessed = true;
    			}
    		}
    
    		// Eagerly cache singletons to be able to resolve circular references
    		// even when triggered by lifecycle interfaces like BeanFactoryAware.
    		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));
    		}

    总结:在放入缓存前调用getBean(),导致缓存中没有,所以每次调用getbean()都会走beforeSingletonCreation()方法,在第二次调用时判断以下条件时(!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName))返回true,会抛出异常

    多例循环依赖:

    package com.spring.bean;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class CirculeB {
    
        @Autowired
        private CirculeA circuleA;
    }
    
    package com.spring.bean;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class CirculeA {
        @Autowired
        private CirculeB circuleB;
    
    }
    
     @org.junit.Test
        public void test4(){
            ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
            CirculeA bean = context.getBean(CirculeA.class);
            CirculeB bean1 = context.getBean(CirculeB.class);
            System.out.println("spring启动成功");
        }

    会报错:org.springframework.beans.factory.UnsatisfiedDependencyException:
     Error creating bean with name 'circuleA': Unsatisfied dependency expressed through field 'circuleB'; 
     nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
     Error creating bean with name 'circuleB': Unsatisfied dependency expressed through field 'circuleA'; 
     nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
     Error creating bean with name 'circuleA': Requested bean is currently in creation: Is there an unresolvable circular reference?

    原因:第一次调用会在ThreadLocal中存放,在第二次调用以下方法时抛出异常

    展开全文
  • 主要介绍了简单了解Spring循环依赖解决过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 本篇文章主要介绍了Spring循环依赖的三种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Spring循环依赖原理

    2021-12-29 17:26:08
    目录 一、什么是循环依赖? 二、构造器参数循环依赖 三、单例的setter注入循环依赖 四、多例的setter注入循环依赖 ...五、Spring如何解决循环依赖 ...Spring循环依赖分为构造器参数依赖和属性setter依赖,先来说说构造

    目录

    一、什么是循环依赖?

    二、构造器参数循环依赖

    三、单例的setter注入循环依赖

    四、多例的setter注入循环依赖

    五、Spring如何解决循环依赖

    六、扩展

    七、总结


    一、什么是循环依赖?

    循环依赖,指的是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。

    • 直接依赖: 如A依赖B,B依赖A。

    image.png

    • 间接依赖: 如A依赖B,B依赖C,C依赖A。

    image.png

    二、构造器参数循环依赖

    Spring循环依赖分为构造器参数依赖和属性setter依赖,先来说说构造器参数导致的循环依赖。

    首先我们定义两个Bean对象:

    public class A {
    	private B b;
    
    	public A(B b) {
    		this.b = b;
    	}
    }
    
    public class B {
    	private A a;
    
    	public B(A a) {
    		this.a = a;
    	}
    }

    然后在Spring配置文件里面注册两个Bean,并指定通过构造参数进行注入:

    <!--构造器注入-->
    <bean id="a" class="com.wsh.circularreference.A">
        <constructor-arg index="0" ref="b"/>
    </bean>
    
    <bean id="b" class="com.wsh.circularreference.B">
        <constructor-arg index="0" ref="a"/>
    </bean>

    编写测试类:

    public class CircularReferenceDemo {
      public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
        System.out.println(applicationContext.getBean("a"));
      }
    }

    我们运行测试类,可以看到控制台抛出BeanCurrentlyInCreationException异常,详细报错信息为:

    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?

    上述例子中,Bean A依赖了Bean B,Bean B反过来也依赖了Bean A,这种情况就是发生了循环依赖。当通过构造器注入的时候,Spring将无法决定先创建哪个bean,所以Spring将产生异常BeanCurrentlyInCreationException。

    Spring并没有直接解决因为构造器注入而导致的循环依赖问题,原因如下:

    Java对类进行实例化的时候,需先实例化构造器的参数,Spring在创建Bean A的时候,构造器需要注入Bean B,那么Spring又会去创建Bean B,然后执行Bean B的构造器时,又发现需要实例化Bean A,从而就构成了一个环,导致Spring无法创建对象,所以就抛出BeanCurrentlyInCreationException异常。

    不过,Spring提供了延迟加载机制来解决构造器循环依赖启动报错的问题。

    三、单例的setter注入循环依赖

    修改前面的实体类,生成对应的setter方法:

    public class A {
    	private B b;
    
    	public void setB(B b) {
    		this.b = b;
    	}
    }
    
    public class B {
    	private A a;
    
    	public void setA(A a) {
    		this.a = a;
    	}
    }
    

    Spring配置文件中,指定采用setter方式进行注入,并且指定bean的作用域为singleton:

    <!--setter注入 单例方式-->
    <bean id="a" class="com.wsh.circularreference.A" scope="singleton">
        <property name="b" ref="b"/>
    </bean>
    
    <bean id="b" class="com.wsh.circularreference.B"  scope="singleton">
        <property name="a" ref="a"/>
    </bean>

    同样运行测试类,观察控制台输出:com.wsh.circularreference.A@255316f2

    我们看到,通过setter方式注入引起的循环依赖,并没有报BeanCurrentlyInCreationException异常,这是因为Spring默认帮我们解决了循环依赖,setter注入导致的循环依赖也是最常见的一种方式,后面我们会详细分析Spring是如何帮我们解决的。

    四、多例的setter注入循环依赖

    在Spring配置文件中,我们指定采用setter方式进行注入,但是指定bean的作用域为prototype:

    <!--setter注入 多例方式-->
    <bean id="a" class="com.wsh.circularreference.A" scope="prototype">
      <property name="b" ref="b"/>
    </bean>
    
    <bean id="b" class="com.wsh.circularreference.B"  scope="prototype">
      <property name="a" ref="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?

    前面提到,Spring是通过提前暴露单例bean的对象工厂出去给其他Bean先使用,它有一个缓存存放着,但是对于"prototype"作用域的bean,Spring容器并不进行缓存,每次都会生成一个新对象,所以Spring无法通过提前暴露解决"prototype"作用域的setter循环依赖

    五、Spring如何解决循环依赖

    前面对三种循环依赖做了一个简单的演示,下面我们针对单例的setter注入导致的循环依赖,跟踪一下源码分析Spring内部是如何解决的。

    在了解Spring循环依赖之前,需要知道Spring创建bean的三个重要的步骤:

    1. 实例化bean:createBeanInstance();
    2. 属性填充:populateBean();
    3. 初始化bean:initializeBean();

    Spring解决循环依赖其实就是在上述的三个步骤处理的。

    思路:

    Spring主要是基于内部的三级缓存来解决循环依赖问题的,这里的三级缓存,其实也就是三个Map,它们之间并不存在上下级关系。

    举个例子A -> B、B -> A,在实例化A的时候调用doGetBean("A"),然后进行实例化A,在实例化完A对象后,Spring通过提前把这个刚实例化好的A对象给暴露出去(实际上就是存放在一个Map中),然后执行到属性填充,发现其依赖了B的实例,此时又会调用doGetBean("B"),然后进行实例化B,同样的,实例化完B后,也会把B对象存放在Map中提前暴露出去,接着执行到B对象的属性填充,发现其依赖了A,那么此时又执行doGetBean("A"),这个时候并不是重新实例化A对象了,而是从前面提到的Map中去获取,也就是拿到提前暴露的对象,这样B对象属性就填充完了,可以执行接下来的初始化过程,B对象初始化完成后,再回过来执行A对象的属性填充,也就可能获取到B对象了,这样就解决了循环依赖的问题。

    简单的流程图大体如下:

    未命名文件 (24).png

    下面从Spring源码的角度看一下,具体是怎么处理的,还是以前面的A -> B,B -> A的例子进行分析。

    • (一)、getBean("a")

    当我们执行applicationContext.getBean("a")这句代码时,进入到AbstractBeanFactory#doGetBean()方法执行,在AbstractBeanFactory#doGetBean()方法中,会执行Object sharedInstance = getSingleton(beanName):尝试从缓存中获取对应的bean。具体代码如下:

    public Object getSingleton(String beanName) {
        // 尝试从缓存中加载bean对象
        // 第二个参数:表示是否允许bean早期依赖
        return getSingleton(beanName, true);
    }
    
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // Quick check for existing instance without full singleton lock
    
        // 尝试从一级缓存中获取对应的bean对象
        Object singletonObject = this.singletonObjects.get(beanName);
        // 如果一级缓存中不存在,并且当前单例bean处于正在创建中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            // 如果一级缓存中没有的话,再尝试从二级缓存中获取对应的bean对象
            // 从早期单例对象缓存earlySingletonObjects中获取单例对象(之所称为早期单例对象,是因为earlySingletonObjects里的对象的都是通过提前曝光的ObjectFactory对象工厂创建出来的,还未进行属性填充等操作)
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 加锁
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            // 如果二级缓存中也没有的话,再尝试从三级缓存中获取对应的ObjectFactory单例工厂
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    
                            // 如果存在单例工厂,则调用单例工厂ObjectFactory的getObject()方法,创建一个bean
                            if (singletonFactory != null) {
    
                                // 调用ObjectFactory的getObject()方法,产生一个半成品bean
                                // 因为添加三级缓存的时候执行的是: addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)),
                                // 所以singletonFactory.getObject()真正执行的其实是:获取bean的早期引用  ===>  getEarlyBeanReference(beanName, mbd, bean)
                                singletonObject = singletonFactory.getObject();
    
                                /**
    								 * 如下可以看到,二级缓存earlySingletonObjects和三级缓存singletonFactories是互斥的
    								 */
                                // 将bean放置于二级缓存中(放到早期单例对象缓存中)
                                this.earlySingletonObjects.put(beanName, singletonObject);
    
                                // 删除三级缓存中对应bean的单例工厂ObjectFactory
                                // 因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存(二级缓存)中了,所以后面获取beanName的单例对象的时候,
                                // 可以从earlySingletonObjects二级缓存拿到,不需要再用到该单例工厂
                                this.singletonFactories.remove(beanName);
    
                            }
                        }
                    }
                }
            }
        }
        // 返回单例对象
        return singletonObject;
    }

    从前面的代码中,可以看到Spring获取bean对象的流程是,首先从一级缓存singletonObjects中查询是否存在指定的bean,如果没有找到,会尝试从二级缓存earlySingletonObjects中查询,如果还是没找到,再尝试从三级缓存singletonFactories中查找。

    getSingleton()方法是Spring解决循环依赖的核心,里面使用了三级缓存,分别是:

    未命名文件 (25).png

     三级缓存其实就是三个Map:

    // 一级缓存:用于保存beanName和创建bean实例之间的关系,beanName -> bean instance
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二级缓存:用于保存beanName和创建bean实例之间的关系,beanName -> bean instance
    //  与一级缓存的区别:当一个单例bean被放在二级缓存中后,当bean还在创建过程中,就可以通过getBean方法获取到了,目的是用来检测循环引用
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    // 三级缓存:用于保存beanName和创建bean的工厂之间的关系,beanName -> ObjectFactory
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    下面是跟踪代码实现:

    image.png

    因为是新创建a对象,所以一级缓存singletonObjects为空,并且isSingletonCurrentlyInCreation("a")返回false。

    所以Object sharedInstance = getSingleton(beanName)方法执行完后,返回的sharedInstance为空,如下图:

    image.png

    isSingletonCurrentlyInCreation():判断当前单例bean是否正在创建中。如在A的populateBean过程中依赖了B对象,此时得先去创建B对象,这时的A就是处于创建中的状态;

    allowEarlyReference:是否允许从singletonFactories中通过getObject拿到对象;

    • (二)、执行getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法

    执行完前面的getSingleton()方法后,在下面又会执行其另一个重载的getSingleton()方法,代码如下:

    if (mbd.isSingleton()) {  //单例作用域
        // 第二个参数是一个ObjectFactory,是一个函数式接口,当调用ObjectFactory的getObject()方法的时候,实际上调用的是createBean(beanName, mbd, args)
        // 也就是说在getSingleton()方法内部调用ObjectFactory的getObject()方法的时候,会回调到这里的createBean(beanName, mbd, args)创建bean,接着才会调用下面的getObjectForBeanInstance()方法
    
        // 8、第八步:尝试从缓存中获取对应的bean实例,获取不到的话,则执行singletonFactory的回调 -> createBean()创建bean
        sharedInstance = getSingleton(beanName, () -> {
            try {
    
                // 创建bean对象
                return createBean(beanName, mbd, args);
            } catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                // 创建失败则销毁
                destroySingleton(beanName);
                throw ex;
            }
        });
        // 返回beanName对应的实例对象
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

    进入getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法,同样的,在一级缓存singletonObjects中找不到实例a。

    接着往下执行beforeSingletonCreation("a"),将"a"加入到了正在创建中的bean集合singletonsCurrentlyInCreation中,如下图:

    image.png

    • (三)、执行singletonObject = singletonFactory.getObject()回调

    接着前面的代码,接着执行singletonFactory.getObject()回调,这里其实是调用的入参createBean("a"),执行创建对象a的流程。如下图: 

    image.png

    接着会进入doCreateBean("a")真正创建a对象,然后通过createBeanInstance()实例化a对象。如下图:

    image.png

    •  (四)、boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName))

    这里是判断bean是否需要提前暴露:

    • mbd.isSingleton():bean是单例的;
    • this.allowCircularReferences:允许循环依赖;
    • isSingletonCurrentlyInCreation(beanName):对象a是否正在创建中;

    因为前面已经把"a"加入到正在创建中bean缓存singletonsCurrentlyInCreation中了,所以这里isSingletonCurrentlyInCreation(beanName))返回true,也就是earlySingletonExposure返回为true,表示需要提前暴露刚刚实例化好的a对象

    • (五)、addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))提前暴露对象

    earlySingletonExposure如果为true的话,会执行提前暴露a对象的逻辑:

    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)),代码如下:

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        // 加锁
        synchronized (this.singletonObjects) {
            // 1、如果一级缓存中不存在当前beanName的时候,才能进if判断
            if (!this.singletonObjects.containsKey(beanName)) {
                // 2、将beanName => ObjectFactory的映射关系添加到三级缓存中,注意添加的是创建bean的对象工厂singletonFactory
                this.singletonFactories.put(beanName, singletonFactory);
                // 3、从二级缓存中移除当前beanName
                this.earlySingletonObjects.remove(beanName);
                // 4、将beanName添加到已注册单例集合中
                this.registeredSingletons.add(beanName);
            }
        }
    }

    通过这个方法,将创建a对象的对象工厂ObjectFactory存入了三级缓存中,并移除二级缓存中对应的缓存。如下图:

    image.png

    addSingletonFactory()方法发生在createBeanInstance()实例化对象之后,也就是说单例对象的构造器已经被调用了,实例已经被创建出来。虽然这个对象还没完成属性填充和初始化过程,属于一个半成品对象,但是Spring将创建这个bean的对象工厂提前暴露了出来,这样其他对象就可以拿到提前暴露出来的对象工厂进行创建对象,然后进行属性填充。

    • (六)、populateBean(beanName, mbd, instanceWrapper)属性填充

    提前暴露完成后,会执行populateBean("a"),填充对象a的属性,发现其依赖了b对象,此时需要执行getBean("b")从容器中获取b对象,跟前面类似,执行doGetBean("b")和Object sharedInstance = getSingleton("b"),如下图:

    image.png

    同样的,因为第一次创建b对象,所以singletonObjects一级缓存、singletonsCurrentlyInCreation中都不存在b对象。

    • (七)、执行getSingleton("b", ObjectFactory<?> singletonFactory)
    if (mbd.isSingleton()) {  //单例作用域
        // 第二个参数是一个ObjectFactory,是一个函数式接口,当调用ObjectFactory的getObject()方法的时候,实际上调用的是createBean(beanName, mbd, args)
        // 也就是说在getSingleton()方法内部调用ObjectFactory的getObject()方法的时候,会回调到这里的createBean(beanName, mbd, args)创建bean,接着才会调用下面的getObjectForBeanInstance()方法
    
        // 8、第八步:尝试从缓存中获取对应的bean实例,获取不到的话,则执行singletonFactory的回调 -> createBean()创建bean
        sharedInstance = getSingleton(beanName, () -> {
            try {
    
                // 创建bean对象
                return createBean(beanName, mbd, args);
            } catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                // 创建失败则销毁
                destroySingleton(beanName);
                throw ex;
            }
        });
        // 返回beanName对应的实例对象
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

    接着,进入getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法,跟前面类似,这里会将"b"加入到正在创建中bean集合缓存singletonsCurrentlyInCreation中,如下图:

    image.png

    •  (八)、singletonObject = singletonFactory.getObject()执行回调

    接着,会执行singletonObject = singletonFactory.getObject(),实际上是执行入参回调createBean("b")创建b对象的流程,如下图:

    image.png

    • (九)、createBeanInstance("b"):实例化b对象 

    image.png

    接着执行到:

    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));

    因为"b"前面已经加入到singletonsCurrentlyInCreation缓存中,所以这里earlySingletonExposure返回true,表示需要提前暴露b对象,则执行:

    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))提前暴露,如下图:

    image.png

    同样的,将创建b的对象工厂加入到了三级缓存singletonFactories中。

    • (十)、populateBean("b", mbd, instanceWrapper)属性填充

    提前暴露完成后,执行populateBean(beanName, mbd, instanceWrapper)对b进行属性填充,发现其依赖了a,所以通过getBean("a")去容器中找对象a,这里又回到doGetBean("a")方法了,再一次执行Object sharedInstance = getSingleton(beanName):

    image.png

    我们发现,因为前面已经将a对象对应的对象工厂ObjectFactory都添加到三级缓存中了,所以这里能拿到a对象对应的ObjectFactory。

    获取到对象工厂后,执行singletonObject = singletonFactory.getObject()回调,实际上是执行:getEarlyBeanReference("a")获取提前暴露的对象a的引用。如下图:

    image.png

    image.png

    执行完singletonObject = singletonFactory.getObject()回调后,接下来会执行下面的代码:

    • 将对象a添加到二级缓存中;
    • 移除三级缓存中对象a对应的ObjectFactory对象工厂;
    // 将bean放置于二级缓存中(放到早期单例对象缓存中)
    this.earlySingletonObjects.put(beanName, singletonObject);
    
    // 删除三级缓存中对应bean的单例工厂ObjectFactory
    // 因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存(二级缓存)中了,所以后面获取beanName的单例对象的时候,
    // 可以从earlySingletonObjects二级缓存拿到,不需要再用到该单例工厂
    this.singletonFactories.remove(beanName);

     如下图: 

    image.png

    执行完前面一系列的过程,已经获取到对象a了,此时对象b的属性填充过程已经完成了,接下来就可以执行initializeBean("a")初始化a对象了,这里就不详细介绍了,跟循环依赖关联不大。

    • (十一)、addSingleton(beanName, singletonObject)

    b对象初始化完成后,会执行addSingleton("b", singletonObject):

    image.png

     具体代码如下:

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // 将bean保存到一级缓存中
            this.singletonObjects.put(beanName, singletonObject);
            // 删除三级缓存对应的bean
            this.singletonFactories.remove(beanName);
            // 删除二级缓存中对应的bean
            this.earlySingletonObjects.remove(beanName);
            // 记录当前所有已注册的bean
            this.registeredSingletons.add(beanName);
        }
    }

    执行完addSingleton()方法后,三个缓存对应的值如下图所示:

    image.png

    可以看到,b对象初始化完成后,被加入到了一级缓存中,同时从三级缓存中移除掉b对象对应的对象工厂。

    • (十二)、a初始化完成

    b初始化完了,接着回来给a对象的b属性赋值,由于前面已经把对象b存入一级缓存中了,所以getSingleton("b")可以直接拿到b对象,直接进行属性填充,这样a对象也经历后续的初始化过程,初始化完成后,也会调用addSingleton("a")将对象a添加到一级缓存中,同时移除二级缓存、三级缓存中对应的缓存。

    至此,循环依赖就解决了。

    六、扩展

    这里主要是总结一下Spring循环依赖常见的一些问题。

    【1】为什么Spring不能解决构造器的循环依赖?

    从前面的分析可以看到,单例bean是在实例化后,也就是执行了构造方法后,才提前暴露的,因此使用构造器注入的话,三级缓存中并没有存放提前暴露的对象,因此getBean的时候,都不能从缓存中获取,所以Spring无法解决构造器参数注入导致的循环依赖。

    【2】为什么prototype原型Bean不能解决循环依赖?

    作用域为prototype的Bean并不是在容器启动的时候开始bean的生命周期的,而是在用到的时候调用doGetBean方法才会走生命周期流程,从源码中可以看到,只有单例bean才使用了三级缓存,原型bean是没有缓存的,所以Spring无法解决prototype原型bean属性注入导致的循环依赖。

    【3】如果只有一级缓存,能不能解决循环依赖问题?

    不能。我们都知道一级缓存存放的是完整对象(实例化完成、属性填充完成、初始化完成),如果只有一级缓存的话,意味着半成品对象(实例化完成、属性未填充、未初始化)需要跟完整对象放在一起,这样调用getBean()就有可能拿到的是半成品对象,属性的值都是null。所以只有一级缓存的话,是不能解决循环依赖问题的。

    【4】如果只有一级缓存、三级缓存的话,能不能解决循环依赖问题?

    只使用一级缓存、三级缓存这两个缓存确实可以解决循环依赖,但是有一个前提,这个bean没被AOP进行切面代理。

    • 如果不涉及到代理的话,只有一级缓存、三级缓存是可以解决循环依赖的。一级缓存存放完整对象,三级缓存存放提前暴露出来的对象。
    • 但是如果涉及到代理的时候,就不行了,因为三级缓存中的ObjectFactory每执行一次,就会新创建一个对象,不能解决循环依赖问题。

    【5】为什么需要使用二级缓存earlySingletonObjects?

    如果没有涉及到AOP代理,二级缓存好像显得有点多余。但是如果使用了AOP代理,那么二级缓存就发挥作用了。前面提到,三级缓存singletonFactories中存放的是ObjectFactory对象工厂,当执行singleFactory.getObject()回调的时候,实际上会执行getEarlyBeanReference()方法获取bean的早期引用,但是我们需要注意的是,每次执行singleFactory.getObject()方法都会重新产生一个新的代理对象,这就有问题了,因为我们的bean是单例的,不可能每次都来一个新的代理对象。

    所以,Spring引入了二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存earlySingletonObjects中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。

    所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。

    【6】三级缓存中为什么保存ObjectFacory对象,而不是保存原始的实例对象?

    举个例子,假设直接将原始对象X存入三级缓存中,那么其他对象如Y依赖了X,在注入的时候,Y需要的X不是原始的X对象,这样就可能有问题。

    如果存入的是一个ObjectFactory对象工厂,那么我们可以根据ObjectFactory生产任何bean对象,ObjectFactory是Spring留给我们进行扩展的,有可能我们需要对bean进行代理或者其他操作。如当调用singletonFactory.getObject()方法的时候,会执行getEarlyBeanReference()方法,里面可以通过BeanPostProcessor后置处理器增强bean。

    七、总结

    根据以上的分析,大概清楚了Spring是如何解决循环依赖的,读者在分析循环依赖的前提,最好对Spring创建bean的流程有一个比较好的了解,这样对循环依赖梳理起来就比较容易。

    最后,通过一张图来总结一下Spring解决循环依赖的详细过程,这张图是笔者跟踪循环依赖处理流程时画的,图中涉及细节比较多,建议读者对照着源码一点点看图,相信多看几遍就能理清其中的处理逻辑了。

    t.png

     希望本篇文章对大家有所帮助,最后也希望读者能指出文章不对之处。

    展开全文
  • 主要给大家介绍了关于Spring循环依赖的正确性,以及Bean注入的顺序关系的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
  • Spring循环依赖问题

    2022-06-05 17:38:58
    spring循环依赖

    一、spring循环依赖

    Spring解决循环依赖是有前置条件的

    出现循环依赖的Bean必须要是单例
    依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决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解决循环依赖(三级缓存)

    (1)、三级缓存定义

    看源码的DefaultSingletonBeanRegistry中有三个 Map 对象

    	/** 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 HashMap<>(16);
    

    singletonObjects:存放初始化后的单例对象,也就是完成的 bean 对象
    earlySingletonObjects:存放实例化,未完成初始化的单例对象(未完成属性注入的对象),也是用来解决性能问题
    singletonFactories:存放 ObjectFactory 对象,存放的是工厂对象,也是用来解决 aop 的问题

    (2)、核心方法refresh(刷新)

    finishBeanFactoryInitialization方法
    重点解析:解决循环依赖。
    三级缓存可以解决循环依赖。

    • singletonObjects:一级缓存(单例池:存放经历了完整生命周期的bean对象)

    • earlySingletonObjects:二级缓存(存放早期暴露出来的bean对象,bean生命周期尚未结束)

    • singletonFactories:三级缓存----单例工厂(存放可以生成bean工厂–准备生产)
      3个map+四个方法(解决循环依赖)

    • getSingleton

    • doCreateBean:添加bean

    • populateBean:属性填充

    • addSingleton:将bean设置到一级缓存,且删除二级和三级
      在这里插入图片描述

    展开全文
  • 1. 什么是循坏依赖 很简单,其实就是互相依赖对方,比如,有一个A对象...如果不考虑Spring循环依赖并不是问题,因为对象之间相互依赖注入是很正常的事情。 比如: 这样,A,B就互相依赖上了。 但是,在Spring
  • Spring循环依赖

    千次阅读 2021-08-14 11:42:16
    Spring循环依赖 什么是Spring循环依赖? 这个很好理解,就是多个bean之间相互依赖,形成了一个闭环。 比如:A依赖于B、B依赖于C、C依赖于A。 代码中表示: public class A{ B b; } public class B{ C c; } public ...
  • Spring循环依赖详解

    千次阅读 2020-11-21 21:13:10
    Spring循环依赖详解什么是循环依赖spring是如何解决循环依赖循环源码分析getSingletion方法getSingletonspring开启代理对象的地方循环依赖的限制条件 什么是循环依赖 今天这边来聊下spring中的循环依赖,在spring的...
  • 冰河花了三个小时画了一张图,就为让你彻底理解Spring循环依赖问题,建议收藏!!
  • 想彻底弄清楚spring循环依赖问题,首先得弄清楚 循环依赖是如何发生的,spring又是如何检测循环依赖的发生的。 其次再探究spring如何解决循环依赖的问题 最后我们将总结循环依赖解决的2个关键因素,提前曝光和曝光...
  • 通常来说,如果问Spring容器内部如何解决循环依赖,一定是指默认的单例Bean中,属性互相引用的场景。 两种注入方式对循环依赖的影响 循环依赖官网说明: 结论: 我们AB循环依赖问题只要A的注入方式是setter且...
  • spring循环依赖详解

    2022-01-12 13:44:17
    填充原始对象中的属性(依赖注入); 如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象; 把最终生成的代理对象放入单例池(源码中叫做singletonObjects)中,下次getBean时就直接从单
  • 详解Spring循环依赖

    千次阅读 2022-04-10 18:35:31
    什么是循环依赖 循环依赖,就是两个或则两个以上的bean互相依赖对方,最终形成闭环。比如“A对象依赖B对象,而B对象也依赖A对象”,或者“A对象依赖B对象,B对象依赖C对象,C对象依赖A对象”;类似以下代码: ...
  • Spring 循环依赖问题的解决方法

    千次阅读 2022-04-11 22:39:29
    Spring通过三级缓存解决了循环依赖。 singletonObjects:一级缓存,存储的是所有创建好了的单例Bean earlySingletonObjects:二级缓存,存储的是完成实例化,但是还未进行属性注入及初始化的对象 ...
  • spring循环依赖及解决方法

    万次阅读 多人点赞 2021-01-27 15:12:13
    ①构造器的循环依赖:这种依赖spring是处理不了的,直接抛出BeanCurrentlylnCreationException异常。 ②单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖,能处理。 ③非单例循环依赖:无法处理。原型...
  • Spring getBean过程如上时序图所示,AbstractBeanFactory第一次调用getSingleton方法入参为beanName: 如果未查询到Bean信息那么就会二次调用getSingleton方法,入参为beanName和ObjectFactory: 这里用了一个...
  • 在AService和BService两个循环依赖的时候 @Component public class AService { private BService bService; public AService(BService bService) { this.bService=bService; } } @Component public ...
  • 主要为大家详细介绍了spring循环依赖策略,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Spring 循环依赖问题及解决方案
  • 1. 循环依赖是什么? Bean A 依赖 B,Bean B 依赖 A这种情况下出现循环依赖。 Bean A → Bean B → Bean A 更复杂的间接依赖造成的循环依赖如下。 Bean A → Bean B → Bean C → Bean D → Bean E → Bean A 2. ...
  • 之前简单讲过Spring循环依赖的解决办法,但是没有深入源码分析,今天源码相关分析来了。 什么是循环依赖? 循环依赖问题就是A->B->A,spring在创建A的时候,发现需要依赖B,因为去创建B实例,发现B又依赖于A,又去...
  • 要完全理解循环依赖,需要理解代理对象的创建时机 掌握ProxyFactory创建代理的过程,理解Advisor, Advice, Pointcut 与Aspect 掌握AnnotationAwareAspectJAutoProxyCreator筛选Advisor合格者,创建代理的过程 1、...
  • Spring循环依赖场景有: (1)构造器的循环依赖 (2)field属性的循环依赖。 1. filed 属性注入的循环依赖代码 @Service public class A { @Autowired private B b; } @Service public class B { @Autowired ...
  • 一文彻底学会spring循环依赖

    千次阅读 2021-11-13 10:24:42
    一文彻底学会spring循环依赖概念前提条件我们在哪源码分析真正的开始 getBeanpublic Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)Object sharedInstance = getSingleton(bean...
  • 问:Spring如何解决循环依赖? 答:Spring通过提前曝光机制,利用三级缓存解决循环依赖(这原理还是挺简单的,参考:三级缓存、图解循环依赖原理) 再问:Spring通过提前曝光,直接曝光到二级缓存已经可以解决循环...
  • 获取bean时先从单例池获取,如果没有则创建并添加到单例池
  • Spring循环依赖的三种方式以及解决办法 【转】https://www.cnblogs.com/liuqing576598117/p/11227007.html 示例 ...一、什么是循环依赖? 循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 71,056
精华内容 28,422
关键字:

spring循环依赖

spring 订阅
友情链接: GA-BP预测.rar