zoukankan      html  css  js  c++  java
  • 【spring源码系列】之【Bean的循环依赖】

    希望之光永远向着目标清晰的人敞开。

    1. 循环依赖概述

    循环依赖通俗讲就是循环引用,指两个或两个以上对象的bean相互引用对方,A依赖于B,B依赖于A,最终形成一个闭环。

    Spring循环依赖的场景有两种:

    • 构造器的循环依赖
    • field 属性的循环依赖
      对于构造器的循环依赖,Spring 是无法解决,只能抛出 BeanCurrentlyInCreationException 异常;对于field 属性的循环依赖,Spring 只解决 scope 为 singleton 的循环依赖,对于scope 为 prototype 的 bean Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。下面重点分析属性依赖的情况。

    2. 循环依赖执行流程


    以上流程图针对如下代码进行演示:

    @Component
    public class CircularRefA {
    
        public CircularRefA() {
            System.out.println("============CircularRefA()===========");
        }
    
        //这里会触发CircularRefB类型的getBean操作
        @Autowired
        private CircularRefB circularRefB;
    }
    
    @Component
    public class CircularRefB {
    
        public CircularRefB() {
            System.out.println("============CircularRefB()===========");
        }
    
        //又会触发A的getBean操作
        @Autowired
        private CircularRefA circularRefA;
    }
    

    step1: A类实例化执行,第一次三个缓存中都没有,会走doGetBean一路走到createBeanInstance,完成无参构造函数实例化,并在addSingletonFactory中设置三级缓存;
    step2:A类populateBean进行依赖注入,随后触发了B类属性的getBean操作;
    step3: B类与A类类似,第一次三个缓存中也都没有,无参构造函数实例化后,设置三级缓存,将自己加入三级缓存;
    step4:B类populateBean进行依赖注入,这里触发了A类属性的getBean操作;
    step5: A类之前正在创建,此时已经是第二次进入,由于一级二级缓存中都没有,会从三级缓存中获取,并且允许 bean提前暴露则从三级缓存中拿到对象工厂,从工厂中拿到对象成功后,升级到二级缓存,并删除三级缓存;若以后有别的类引用的话就从二级缓存中进行取;
    step6:B类拿到了A的提前暴露实例注入到A类属性中了,此时完成B类的实例化;
    step7:A类之前依赖B类,B的实例化完成,进而促使A的实例化也完成,并且此时A的类B属性已经有值,A类继续走后续的afterSingletonCreateion与addSingleton方法,删除正在创建缓存中的实例,并将实例从二级缓存移入以及缓存,同时删除二三级缓存;

    以上是A类实例化的全过程,下面会针对源码逐一分析。

    3. 源码分析

    首先创建A的实例,需要从A的getBean方法开始,到doGetBean,第一次优先从缓存中取,进入getSingleton方法:

    	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    		// 从一级缓存singletonObjects中取
    		Object singletonObject = this.singletonObjects.get(beanName);
    		// 一级缓存为空,且单例对象正在创建
    		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    			synchronized (this.singletonObjects) {
    				// 从二级缓存earlySingletonObjects中取
    				singletonObject = this.earlySingletonObjects.get(beanName);
    				// 如果二级缓存也为空,且允许bean提前暴露
    				if (singletonObject == null && allowEarlyReference) {
    					// 从三级缓存singletonFactories中取
    					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    					// 如果三级缓存不为空
    					if (singletonFactory != null) {
    						// 调用getEarlyBeanReference方法提前暴露bean
    						singletonObject = singletonFactory.getObject();
    						// 提前暴露的bean放入二级缓存,
    						this.earlySingletonObjects.put(beanName, singletonObject);
    						// 将提前暴露的bean从三级缓存中删除
    						this.singletonFactories.remove(beanName);
    					}
    				}
    			}
    		}
    		return singletonObject;
    	}
    

    这个方法主要是从三个缓存中获取,分别是:singletonObjectsearlySingletonObjectssingletonFactories,三者定义如下:

    	/** 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:单例对象的cache
    • singletonFactories : 单例对象工厂的cache
    • earlySingletonObjects :提前暴露的单例对象的Cache

    解决循环依赖的关键是三个缓存,其中一级缓存singletonObjects存放完全实例化的对象,对象以及其依赖的属性都有值;二级缓存earlySingletonObjects存放半实例化的对象,相当于在内存开辟了空间,已完成创建,但是还未进行属性赋值,可以提前暴露使用;三级缓存singletonFactories为对象工厂,用来创建提前暴露的bean并放入二级缓存中。

    首次初始化A时,三个缓存中都没有对象,会进入如下getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法,该方法第二个参数是个函数式接口,当内部调用getObject方法时,会调用createBean方法:

    				// 创建bean实例
    				if (mbd.isSingleton()) {
    					sharedInstance = getSingleton(beanName, () -> {
    						try {
    							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;
    						}
    					});
    					......
    				
    

    先进入getSingleton方法:

    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) {
    				if (this.singletonsCurrentlyInDestruction) {
    					throw new BeanCreationNotAllowedException(beanName,
    							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
    							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
    				}
    				if (logger.isDebugEnabled()) {
    					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    				}
    				// bean单例创建前
    				beforeSingletonCreation(beanName);
    				boolean newSingleton = false;
    				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    				if (recordSuppressedExceptions) {
    					this.suppressedExceptions = new LinkedHashSet<>();
    				}
    				try {
    					// 调用createBean方法创建bean
    					singletonObject = singletonFactory.getObject();
    					newSingleton = true;
    				}
    				catch (IllegalStateException ex) {
    					// Has the singleton object implicitly appeared in the meantime ->
    					// if yes, proceed with it since the exception indicates that state.
    					singletonObject = this.singletonObjects.get(beanName);
    					if (singletonObject == null) {
    						throw ex;
    					}
    				}
    				catch (BeanCreationException ex) {
    					if (recordSuppressedExceptions) {
    						for (Exception suppressedException : this.suppressedExceptions) {
    							ex.addRelatedCause(suppressedException);
    						}
    					}
    					throw ex;
    				}
    				finally {
    					if (recordSuppressedExceptions) {
    						this.suppressedExceptions = null;
    					}
    					// 创建完成后要从正在实例化的bean集合singletonsCurrentlyInCreation中删除该bean
    					afterSingletonCreation(beanName);
    				}
    				if (newSingleton) {
    					// bean加入缓存
    					addSingleton(beanName, singletonObject);
    				}
    			}
    			return singletonObject;
    		}
    	}
    

    该方法完成流程图中的四个步骤:
    step1:bean单例创建前,将beanName放入singletonsCurrentlyInCreation缓存;
    step2: singletonFactory.getObject()调用外面函数式接口中的createBean方法创建bean;
    step3afterSingletonCreation方法将beanName从singletonsCurrentlyInCreation缓存删除,表示已创建完;
    step4: 实例化后加入一级缓存,二三级缓存删除。

    以上1、3、4涉及代码如下:

    	protected void beforeSingletonCreation(String beanName) {
    		// 将正在创建的bean放入缓存singletonsCurrentlyInCreation
    		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
    			throw new BeanCurrentlyInCreationException(beanName);
    		}
    	}
    
    	protected void afterSingletonCreation(String beanName) {
    		// 正在创建中的缓存容器singletonsCurrentlyInCreation清除刚刚创建的bean
    		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
    			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    		}
    	}
    
    	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);
    			//
    			this.registeredSingletons.add(beanName);
    		}
    	}
    

    其中step2流程较长,当A第一次实例化时,走到创建无参构造函数实例化createBeanInstance,随后会走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);
    			}
    		}
    	}
    

    从这段代码我们可以看出singletonFactories这个三级缓存才是解决循环依赖的关键,该代码在createBeanInstance方法之后,也就是说这个bean其实已经被创建出来了,但是它还不是很完美(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(可以根据对象引用定位到堆中对象),能够被认出来了,所以Spring在这个时候选择将该对象提前曝光出来让大家认识认识。当三级缓存有值后,后面如果再次用到该bean的时候,会从三级缓存中,并通过提前暴露,升级到二级缓存中,到这里我们发现三级缓存singletonFactories和二级缓存earlySingletonObjects中的值都有出处了,那一级缓存在哪里设置的呢?就是在A创建完,并把A依赖的属性B也创建完后,B有依赖于A,再次进入A后,A直接从二级缓存中获取,从而促使B对象创建完,随即A也就创建完成,A完成createBean后走上面的step4中的addSingletion方法,完成一级缓存的设置。

    4.总结

    Spring在创建bean的时候并不是等它完全完成,而是在创建过程中将创建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 缓存中),这样一旦下一个 bean 创建的时候需要依赖 bean ,则直接使用 ObjectFactory 的 getObject() 获取了,故在缓存中使用三级缓存获取到实例,并将实例升级到二级缓存,供后续实例如需二次使用时,可直接从二级缓存中取,待实例完全创建后,升级到一级缓存,并清理二级三级缓存,总而言之提前暴露三级缓存,以及一二三级缓存的综合使用是解决循环依赖的关键,各级缓存各司其职,又能够相互呼应,spring的设计实在精妙,给我们自己设计项目提供了一种优秀的思考方式。

  • 相关阅读:
    poj3463 Sightseeing(最短路,次短路计数)
    poj3463 Sightseeing(读题很重要)
    poj3463 Sightseeing(读题很重要)
    hdu6181 Two Paths(次短路)
    hdu6181 Two Paths(次短路)
    Tyvj1293(新姿势:次短路)
    Tyvj1293(新姿势:次短路)
    10.bitset
    9.优先队列,priority_queue
    8.queue
  • 原文地址:https://www.cnblogs.com/father-of-little-pig/p/15035056.html
Copyright © 2011-2022 走看看