Spring(十一):bean的加载——检测循环依赖

回顾

前面我们已经看过了对于XML中的bean、alias、import、beans标签的解析

现在IOC容器中已经有着bean的配置了,下面就来看看,在Spring中,是如何加载这些配置的

bean的加载

bean的加载远远比解析xml文件要难,首先我们来看一下Spring中是如何获取bean的

在这里插入图片描述

从官网上可以看到,使用getBean方法就可以获取到实例Bean了

getBean方法

getBean的方法是位于BeanFactory接口里面的

在这里插入图片描述
这里中调用的是AbstractBeanFactory的getBean

在这里插入图片描述
并且具体的实现在doGetBean方法里面

doGetBean方法

源码如下

  • name:指定的beanName
  • requiredType:指定的beanType
  • args:实例化bean时使用的参数
  • typeCheckOnly:实例化bean是否开启类型检查
	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
		//将name转化为beanName,因为进来的name可能是别名
        //beanName就会从aliasMap中获取,aliasMap就是存放别名的容器
		String beanName = transformedBeanName(name);
		Object beanInstance;

        //这一步是避免循环依赖的问题
        //假设A中有B的属性,而B中有A的属性
        //当依赖注入时,就会产生A还没创建完,就会因为去注入B,导致去创建B
        //而创建B的时候由因为去注入A,从而再次去创建A,造成一个循环问题
        
        //会从singletonObjects(单例实例工厂)和earlySingletonObjects(单例缓存)中去根据beanName去获取bean
        //并且在方法里面,如果找不到,是会添加进earlySingletonObjects(单例缓存)中去的
        //也就是说bean还没有创建完,就会提前将bean的ObjectFactory暴露出来
        //比如当前的bean还没创建,就会提前存入缓存中进行暴露
        //当下个bean创建时需要依赖上个bean则直接使用ObjectFactory
		Object sharedInstance = getSingleton(beanName);
        //假如缓存中或者实例工厂中存在该bean,并且没有给创建bean的参数
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
                //判断该bean是不是已经创建过,通过beanName去判断
                //而且这里的判断是判断创建的单例bean,从前面创建的单例bean中
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
            //从实例工厂中或者本身实例中获取bean实例
   			//也就是说beanInstance可能是实例工厂中创建,也可能直接为sharedInstance
            //这样就解决了单例情况的循环依赖问题,当创建B时去注入属性A时,会从单例缓存或单例工厂中直接取原来的A
            //这样就不会创建另一个A了,解决了单例的循环依赖问题
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		//如果单例缓存中或者实例工厂中不存在该bean
        //证明是原型模式创建该bean
		else {
            //判断是否出现了循环依赖问题
            //上面是解决单例模式的循环依赖,而这里是判断在原型模式情况下是否出现了循环依赖
            //判断beanName对应的bean在缓存中是否已经存在
			if (isPrototypeCurrentlyInCreation(beanName)) {
                //如果已经在创建,抛错
                //所以Spring是不会解决原型模式的循环依赖
				throw new BeanCurrentlyInCreationException(beanName);
			}
			//判断beanName对应的bean是不是已经在工厂中创建
            //获取工厂
			BeanFactory parentBeanFactory = getParentBeanFactory();
            //如果工厂不为空,且IOC容器里面不包含这个bean
            //一般来说只要配置正确是不会走这里的
            //这里是针对在bean标签中引用了其他bean,但其他bean并没有实例化
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}
			//判断是否需要类型检查
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
					.tag("beanName", name);
			try {
				if (requiredType != null) {
					beanCreation.tag("beanType", requiredType::toString);
				}
                //使用IOC容器中的beanDefinition创建RootBeanDefinition
                //但此时该bean是还没有创建好的,因为存在依赖注入其他bean的问题
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
                // 获取该bean需要依赖的其他bean
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
                    //遍历所有依赖的bean
					for (String dep : dependsOn) {
                        //这一步就是判断当前bean与依赖的bean是否有循环依赖
                        //在里面是有一个容器去存储正在依赖的bean的
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
                        //如果不存在循环依赖问题就进行注册
						registerDependentBean(dep, beanName);
						try {
                            //这里调用getBean方法去获取依赖的bean
                            //这里产生了递归,getBean仍然会去调用doGetBean方法
                            //所以循环依赖的检查通过递归、三级缓存和提前暴露去检查出来
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
				//通过上面的检测已经检测出该bean是否出现循环依赖问题了
                //下面就可以正式创建bean了
				// Create bean instance.
                // 如果当前创建的bean是单例模式
				if (mbd.isSingleton()) {
                    //使用单例模式进行创建bean
					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;
						}
					});
                    //创建beanInstance
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				// 如果当前创建的bean是原型模式
				else if (mbd.isPrototype()) {
                    //使用原型模式来创建bean
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
				// 其他模式下创建bean
				else {
                    //其他模式下创建bean
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
                        //创建beanInstance
						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new ScopeNotActiveException(beanName, scopeName, ex);
					}
				}
			}
			catch (BeansException ex) {
				beanCreation.tag("exception", ex.getClass().toString());
				beanCreation.tag("message", String.valueOf(ex.getMessage()));
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			finally {
                //最终释放缓存
				beanCreation.end();
			}
		}
        //使用得到的beanInstace去创建bean
		return adaptBeanInstance(name, beanInstance, requiredType);
	}

总结一下加载bean的过程

  • 通过name获取真实的beanName(SimpleAliasRegistry里面的aliasMap中寻找,前面学习alias标签注册的时候使用过这个容器)

    • 别名转换为beanName
    • 取出FactoryBean的修饰符,因为FactoryBean前面都带有一个&,要将&去除掉
  • 判断当前是否出现单例Bean的循环依赖

    • Spring对于单例bean的创建只会创建一次,以后使用到该bean时都是直接从单例缓存中取的
    • Spring采用三级缓存+提前曝光的形式去创建bean,就是该bean还没创建完(存在依赖注入其他bean),该bean就会被添加进缓存中,因为Spring规定了必须将依赖的bean加载完了,才能去加载父bean
    • 这里会优先从单例bean的缓存中尝试看能不能加载bean
      • 如果能加载出来,证明出现了循环依赖了
      • 那么会直接将缓存的bean返回(解决了单例的循环依赖)
  • 如果单例缓存中加载不出来,那就证明没有单例的循环依赖,接下来判断是不是原型的循环依赖

    • Spring是不会去对原型模式的循环依赖进行解决的,一旦发现是原型模式的循环依赖是会直接抛错的,因为原型模式每次进行使用都会去创建一个新的bean,所以原型模式没有自己的缓存(每次都去创建新的,用缓存干嘛)
    • 不过在创建Bean时,也是会利用缓存的,因为判断是否出现循环依赖的问题是使用的提前曝光
    • 如果没有出现循环依赖问题,才开始去创建bean
      • 单例模式创建bean
      • 原型模式创建bean
      • 其他模式创建bean
  • 最后就是进行类型转换,使用转换器对创建去的bean转化为想要的类型,这也是为什么使用context去getBean时要提供class方法

从缓存中获取单例bean

下面就来看下单例的缓存,这也是解决单例出现循环依赖的关键之处

要注意的是此时该bean还没有创建完,这一步也是进行提前曝光的

getSingleton

对应的代码为下面这一部分

而具体从缓存中去获取单例bean的是在getSingleton方法里面,并且可以看到,单例缓存是交由DefaultSingletonBeanRegistry负责的

在这里插入图片描述
该方法调用了getSingleton方法,并且将allowEarlyReference的参数设置为true,这个参数是是否进行提前曝光的,这里设置为true,就代表进行提前曝光

看源码前,必须先认识这里的5个容器

在这里插入图片描述
三级缓存是指下面这三个

  • 一级缓存:singletonObjects,存储所有的单例bean
  • 二级缓存:earlySingletonObjects,存储提前暴露的bean,里面的bean都是没创建完的
  • 三级缓存:singletonFactories,存储单例bean的ObjectFactory(也就是创建该bean的工厂)
  • registeredSingletons:存储创建好的单例Bean的beanName,可以用这个来判断beanName是否已经创建了
  • singletonsCurrentlyInCreation:存储正在创建的单例bean的beanName,可以用这个来判断指定beanName是不是正在创建

源码如下

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
        // 判断一级缓存中是否有该bean,一级缓存就是创建好的所有单例bean
        // 所以一级缓存中bean出现的话,直接用就好了
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //如果一级缓存中没有,并且判断这个bean是不是正在创建中
            //这里存储正在创建中和的单例beans底层容器是一个set集合
            //从二级缓存中去获取bean
			singletonObject = this.earlySingletonObjects.get(beanName);
            //如果二级缓存中没有,并且允许进行提前暴露
			if (singletonObject == null && allowEarlyReference) {
                //对一级缓存进行加锁,
				synchronized (this.singletonObjects) {
                    //开始从按顺序从三个缓存中取bean
                    //从一级缓存中取
					singletonObject = this.singletonObjects.get(beanName);
                    //一级缓存中没有
					if (singletonObject == null) {
                        //从二级缓存中取
						singletonObject = this.earlySingletonObjects.get(beanName);
                        //二级缓存中没有
						if (singletonObject == null) {
                            //从三级缓存中取出该bean的ObjectFactory
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
                                //通过ObjectFactory取创建bean
								singletonObject = singletonFactory.getObject();
                                //并且将该bean存放进二级缓存中
								this.earlySingletonObjects.put(beanName, singletonObject);
                                //因为二级缓存中有该bean了,所有不需要三级缓存了
                                //从三级缓存中移除这个bean
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
        //返回单例bean
		return singletonObject;
	}

总结一下这个方法

  • 判断一级缓存中是否已经有这个bean(之前是否已经创建好了)

    • 有的话直接使用
  • 一级缓存中没有该bean,并且该bean是正在创建中的

    • 从二级缓存中取出该bean,看是否已经提前暴露了
  • 二级缓存中没有该bean,并且允许进行提前暴露

    • 给一级缓存进行上锁,开始逐层缓存去获取bean,因为只要给一级缓存上锁了,因为是先访问一级缓存的,只要锁上了一级缓存,其他缓存都无法使用
    • 从一级缓存中获取
    • 一级缓存中没有就从二级缓存中获取
    • 二级缓存中没有,从三级缓存获取该bean的ObjectFactory
      • 利用ObjectFactory获取bean,并存放二级缓存中进行提前暴露,并且删除三级缓存,因为二级缓存中已经有了,那么三级缓存就没必要了
  • 所以,最早暴露Bean的是三级缓存,从三级缓存中获取暴露的bean,是会移去二级缓存中去的

  • 最终返回SingleonObject

采用缓存和提前暴露的机制可以解决单例的循环依赖问题,出现循环依赖的时候不会去创建新依赖,而是使用前面正在创建的依赖

原型模式下解决循环依赖

假如单例缓存中没有,即三级缓存并没有提前暴露该bean,那么就要从头进行加载bean了

首先要理解,创建有依赖的bean其实是一个递归过程,我们并不知道此时位于哪一个bean的初始化

所以,在进行从头加载bean时,也要判断单例缓存中有没有,再判断原型模式下是不是出现了循环依赖

具体的源码如下

			//原型模式下
			//判断这个bean是不是已经在创建了
			if (isPrototypeCurrentlyInCreation(beanName)) {
                //如果这个bean已经在创建,那就代表出现了循环引用的问题
                //直接抛错
				throw new BeanCurrentlyInCreationException(beanName);
			}
			//判断bean是不是存在于上级的工厂中
			//先获取上级的工厂
			BeanFactory parentBeanFactory = getParentBeanFactory();
			//判断上级工厂是否未空,并且IOC容器中是否有定义的BeanDefinition
			//一般不会进来这里的
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}
			//进行类型检查
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
			//
			StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
					.tag("beanName", name);
			//下面这里才是真正地去保证bean的成员属性bean是已经初始化好的
			try {
                //判断指定类型是否为空
				if (requiredType != null) {
					beanCreation.tag("beanType", requiredType::toString);
				}
                //从IOC容器中去获取bean的配置
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
                //获取bean的配置的所有依赖
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
                    //遍历所有依赖的bean
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
                        //注册bean的依赖
                        //这一步是提前曝光bean的依赖
						registerDependentBean(dep, beanName);
						try {
                            //递归加载此时的依赖bean
                            //这个方法依然去调用了doGetBean去加载
                            //加载的时候又会去检测是否出现循环依赖
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
                //如果要创建的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;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				//如果要创建的bean是原型的,原型模式创建
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
				//其他模式进行创建
				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new ScopeNotActiveException(beanName, scopeName, ex);
					}
				}
			}
			catch (BeansException ex) {
				beanCreation.tag("exception", ex.getClass().toString());
				beanCreation.tag("message", String.valueOf(ex.getMessage()));
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			finally {
				beanCreation.end();
			}

从代码中可以看到,原型模式下解决循环依赖也是使用提前曝光的方法的

  • 判断当前原型bean是否正在创建中
    • 如果正在创建中就代表之前提前暴露过了,出现了循环依赖的问题
    • 如果没在创建中
      • 从容器中取出要创建的beanDefinition
      • 遍历该beanDefinition的所有依赖bean
      • 将依赖bean进行曝光,表示该bean正在创建
      • 递归调用getBean方法进行加载依赖的bean,因为Spring规定,加载bean必须要保证其依赖的bean要加载完,所以采用递归的方式
      • 递归去调用getBean方法对所有的依赖进行循环依赖检测

下面就逐步去看一下是怎么实现的

判断当前原型Bean是否正在创建中

从代码上看,对应的代码为isPrototypeCurrentlyInCreation

在这里插入图片描述

这个方法的源码如下

	protected boolean isPrototypeCurrentlyInCreation(String beanName) {
        //从本地的TheadLocal中取出正在创建的bean
        //也就是说Spring将正在创建的原型bean存储在ThreadLocal中
        //因为单例bean是全局使用的,必须使用同一个集合
        //但原型bean是使用一次就创建一次,所以不需要使用全局的集合,只需要ThreadLocal存储就行
        //因为哪个线程使用原型bean就这个线程负责创建和销毁即可
		Object curVal = this.prototypesCurrentlyInCreation.get();
        //如果ThreadLocal中不为空,且ThreadLocal中正在创建的bean包含当前的beanName
        //那么就代表当前bean已经在创建了,产生了循环依赖问题
        //而且这里看到,假如只有一个原型bean正在创建,那么ThreadLocal只会存储这个原型bean的beanName
        //如果有多个进行创建,那么ThreadLocal存储的是一个Set集合,并且Set集合里面存储的是正在创建的beanName
		return (curVal != null &&
				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
	}

在这里插入图片描述

从源码上可以知道

  • Spring对于原型bean的创建使用了ThreadLocal作为缓存
  • 当只有一个bean正在创建时,ThreadLocal仅仅存储一个字符串
  • 当有多个bean正在创建时,ThreadLocal存储的是一个Set集合,Set集合里面存的是所有正在创建的beanName
从容器中取出RootBeanDefinitions

在这里插入图片描述

先说明一下RootBeanDefinition,RootBeanDefinition其实也是代表一个BeanDefinition,从注释上可以看出这个RootBeanDefinition是针对有继承关系的Bean的,换句话说,其实RootBeanDefinition代表的是父类!!! 如果没有继承关系,才代表是当前类

方法里面的源码如下

	protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// Quick check on the concurrent map first, with minimal locking.
        //先从mergedBeanDefinitions中取出RootBeanDefinition
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
        //如果容器中有且该RootBeanDefinition不允许进行re-merged操作
        //re-merged操作其实就是合并父类,因为RootBeanDefinition代表的是父类
        //但我们初始化的时候,先初始化的是子类,所以需要进行将父类合并进来
		if (mbd != null && !mbd.stale) {
            //直接返回容器中的bean
			return mbd;
		}
        //如果容器中没有,或者容器中的RootBeanDefinition允许进行合并
        //那就需要进行合并操作,并且返回合并后的RootBeanDefinition
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}

在这里插入图片描述

在这里插入图片描述
从代码上可以看到

  • RootBeanDefinitions代表的是父类,并且是通过合并子类的方式类进行创建的

  • Spring使用了mergedBeanDefinitions容器,为一个初始化容量为256的ConcurrentHashMap来存储RootBeanDefinition

  • RootBeanDefinition通过一个stale变量来控制该bean是否还可以继续合并

  • 如果容器中没有、或者容器中有,但该RootBeanDefinition还允许继续进行合并,那就需要先进行合并,再将合并后的RootBeanDefinition返回

  • 并且合并的bean的配置从IOC容器中去取,得到IOC容器里面的BeanDefinition

合并RootBeanDefinition

在这里插入图片描述

可以看到,其调用了重载的方法,并且调用了

源码如下

protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {
		
    	//先对mergedBeanDefinitions容器进行上锁
    	//避免在进行合并的时候,有人在使用mergedBeanDefinitions
    	//因为合并后是要进行更新替换的,假如多个线程进来都合并这个bean就会产生并发问题
		synchronized (this.mergedBeanDefinitions) {
                        
			RootBeanDefinition mbd = null;
			RootBeanDefinition previous = null;

			// Check with full lock now in order to enforce the same merged instance.
            // 如果containningBd为空,就代表没有要内嵌的bean
            //并且可以看到,这个参数传进来就是为Null的,证明加载bean的时候是没有管内嵌bean的
			if (containingBd == null) {
                //从mergedBeanDefinitions取出旧的RootBeanDefinition
				mbd = this.mergedBeanDefinitions.get(beanName);
			}
			//如果没有,或者允许进行合并
            //旧可以进行下面的合并操作了
			if (mbd == null || mbd.stale) {
         		//使用previous去承载旧的RootBeanDefinition
				previous = mbd;
                //如果要合并的beanDefinition,并没有父类,就是没有Parent
				if (bd.getParentName() == null) {
					// Use copy of given root bean definition.
                    // 那就进行复制即可,不需要进行合并了
					if (bd instanceof RootBeanDefinition) {
                        //如果IOC容器里面的BeanDefinition本来就是RootBeanDefinition类型
                        //进行拷贝,并且这里是是一个浅拷贝
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
                    //如果不是RootBeanDefinition类型的
					else {
                        //使用构造函数去构造RootBeanDefinition
						mbd = new RootBeanDefinition(bd);
					}
				}
                //如果要合并的beanDefinition,是有parent的
                //那就代表这个beanDefinition是一个ChildBeanDefinition
				else {
					// Child bean definition: needs to be merged with parent.
					BeanDefinition pbd;
					try {
                        //同理,去将parentName转化为对应的beanName,因为可能出现别名什么的
                        //签名已经看过这个方法底层了,就不再进行赘述了
						String parentBeanName = transformedBeanName(bd.getParentName());
                        //如果要合并的beanName与parentBeanName不相等
                        //那就证明需要进行合并
						if (!beanName.equals(parentBeanName)) {
                            //可以看到这里递归地去进行合并了
							pbd = getMergedBeanDefinition(parentBeanName);
						}
                        //如果是相等的name
						else {
                            //只能使用父类的BeanFactory来进行实例化父类了
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
												"': cannot be resolved without a ConfigurableBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
					}
					// Deep copy with overridden values.
                    //pbd是经过递归返回的结果,为是最上层的父类
                    //并且进行深拷贝得到最上层的父类
					mbd = new RootBeanDefinition(pbd);
                    //bd此时是子类的beanDefinition
                    //父类要装上子类的一些属性,所以要进行重写
                    //父类mbd从子类bd重写一些属性
                    //其实从这里就可以知道Spring是怎么处理具有继承关系的bean了
                    //通过合并重写的方式去创建有继承关系的bean
                    //通过递归的方式得到父Bean,然后得到的父Bean重写当前子Bean的一些属性
                    //重写子类Bean的属性相当于就是合并了子类Bean
                    //此时RootBeanDefinition就变为了mbd,合并了子类的属性
					mbd.overrideFrom(bd);
				}
				// 给RootBeanDefinition设置范围,默认为单例
				// Set default singleton scope, if not configured before.
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(SCOPE_SINGLETON);
				}
				
				//如果RootBeanDefiniton的范围为单例,并不一定为单例
                //RootBeanDefinition的范围还会受限于containningBd,也就是内嵌的bean
                //假如内嵌的containningBd不是单例,但RootBeanDefinition是单例
                //那么就会RootBeanDefinition的范围以containningBd为准
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                    //将RootBeanDefinition的范围设置为containingBd的范围
					mbd.setScope(containingBd.getScope());
				}
				
				//并且如果没有内嵌bean会进行缓存!!!
                //存入mergedBeanDefinitions容器中
				if (containingBd == null && isCacheBeanMetadata()) {
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
            //
			if (previous != null) {
				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
            //返回创建好的RootBeanDefinition
			return mbd;
		}
	}

总结一下合并RootBeanDefinition的流程

  • 对mergedBeanDefinitions进行上锁(ConcurrentHashMap进行上锁)
  • 从mergedBeanDefinition中去获取旧的RootBeanDefinition
  • 判断旧的RootBeanDefinition是否为空和是否允许合并
  • 从BeanDefinition去获取parentName,并且转化为parentBeanName
  • 如果当前的beanName与parentBeanName不一致,那就代表正常的继承关系
    • 递归去构建parentBeanName的RootBeanDefinition(重要)
  • 如果当前的beanName与parentBeanName一致
    • 那就只能通过parent的BeanFactory去进行创建了
  • 递归得到RootBeanDefinition后,进行一份深拷贝(因为要替换mergedBeanDefinition里面的元素)
  • 并且RootBeanDefinition要重写子类的属性
  • 判断新创建的RootBeanDefinition是否有范围
    • 如果没有范围,默认设置为单例
    • RootBeanDefinition范围还会受限于ContainningBd
      • 如果内嵌Bean有自己的范围并且不是单例,而RootBeanDefinition是单例
      • RootBeanDefinition的范围会改成containningBd的范围
      • 不过在加载的时候,containningBd一般都为Null
  • 最终将新创建的RootBeanDefinition放入mergedBeanDefinitions容器中进行更新,并且返回新创建的RootBeanDefinition
加载依赖bean

获取了RootBeanDefinition之后,下一步就是去假爱依赖的bean了

对应的代码为这一块

在这里插入图片描述

//获取RootBeanDefinitions的依赖
String[] dependsOn = mbd.getDependsOn();
//如果有依赖要注入的bean
if (dependsOn != null) {
    //遍历所有的依赖bean
	for (String dep : dependsOn) {
    	//判端当前依赖的bean和RootBeanDefinition是否出现循环依赖
		if (isDependent(beanName, dep)) {
            //如果出现就会抛出异常
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
			"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
		}
      	//没有出现循环依赖就进行注册依赖的bean
        //这里注册是指注册这个依赖的bean进beanName里面
        //代表beanName指定的bean已经要依赖这个dep了
        //相当于提前暴露
		registerDependentBean(dep, beanName);
		try {
            //递归去加载依赖的bean
            //同理依赖的bean自身也会检查自己有没有出现循环依赖问题
			getBean(dep);
		}catch (NoSuchBeanDefinitionException ex) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
		}
	}
}
获取RootBeanDefinitions的依赖

在这里插入图片描述
可以看到,这是个位于AbstractBeanDefinition的字符数组

在这里插入图片描述
这个数组肯定在前面的合并操作中不停变化的,因为合并操作是一个递归过程,并且每次递归都产生新的RootBeanDefinition

判断当前依赖的bean是否产生重复依赖

在这里插入图片描述
可以看到这里首先给dependentBeanMap进行了上锁

在这里插入图片描述

并且这个dependentBeanMap是一个容量为64的ConcurrentHashMap,并且里面的键值对形式为<String,Set>,key代表beanName,而set集合里面代表该beanName的beanDefinition需要依赖到的bean,并且这是一个LinkedHashSet,后面会提到,所以可以知道这个dependentBeanMap的作用其实就是保存bean和其依赖bean的关系

上锁了之后会调用isDepend的重载方法

源码如下

	private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
        //alreadySeen是用来存储已经检查的依赖
        //也是检查循环依赖的关键,因为是用alreadySeen进行存储当前依赖到bean的所有依赖
		if (alreadySeen != null && alreadySeen.contains(beanName)) {
			return false;
		}
        //转化beanName
		String canonicalName = canonicalName(beanName);
        //取出dependentBeanMap中该beanName对应的依赖bean的set集合
        //针对不同依赖bean之间是否出现循环依赖
		Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        //如果set集合没有,代表该bean没有依赖bean
		if (dependentBeans == null) {
            //返回false
            //代表beanName与dependentBeanName没有冲突
			return false;
		}
        //如果依赖bean集合存在,并且里面已经有依赖这个dependentBeanName了
		if (dependentBeans.contains(dependentBeanName)) {
            //代表已经依赖过了,并且出现了重复依赖
            //注意这里只是重复依赖,并不是循环依赖
			return true;
		}
        //如果依赖bean集合存在,且里面没有依赖这个dependentBeanName
        //那么就需要判断其他依赖的bean中有没有依赖这个dependentBeanName
        //遍历所有依赖bean
		for (String transitiveDependency : dependentBeans) {
            //alreadySeen用来存储已经检查过的依赖bean
            //第一次进来会为空,因为之前并没有检查
            //需要进行初始化
			if (alreadySeen == null) {
                //可以看到alreadySeen是一个HashSet
				alreadySeen = new HashSet<>();
			}
            //将检查完的bean添加进alreadySeen中去
			alreadySeen.add(beanName);
            //递归去检查依赖bean中是不是有依赖dependentBeanName
			if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
                //存在重复依赖,返回true
				return true;
			}
		}
		return false;
	}

从代码中可以看到

  • Spring使用一个临时的HashSet存储所有该依赖bean(传进来的dependentBeanName)的依赖bean

  • 通过HashSet去进行判断是否出现了重复依赖和循环依赖

    • 重复依赖是指:依赖了A bean 但依赖的另一个B Bean却又依赖了A Bean
    • 循环依赖是指:依赖A bean,但A bean又依赖了自己
  • 通过递归的方式,将当前依赖bean的所有依赖,包括依赖的依赖都存储进alreadySeen的set集合中,每次都会判断set集合中是否已经有该依赖了,alreaySeen只是一个临时集合,就是检查当前依赖bean自身使用到的,如果当前依赖bean自身没问题,需要去判断其他同级的依赖bean是否与其有冲突,也就需要一个全局记录依赖bean的容器,说白了就是dependentBeanMap

  • 并且这里要注意,这里仅仅只是判断当前依赖的bean是否产生重复依赖和循环依赖,在前面看到源代码是循环判断所有依赖的bean的

  • 也就是说这里仅仅检查当前依赖的bean的本身是否出现重复依赖

注册依赖bean

经过上面已经将检测完当前该依赖bean是否有产生循环依赖了,如果没有产生,那就需要进行注册依赖bean了,要记得对于依赖bean的操作是一个循环操控所有的依赖bean,前面判断当前依赖的bean是否产生循环依赖,仅仅只是当前依赖的bean,那么对于后面依赖的bean又有没有对之前的bean进行重复依赖呢?比如bean A里面有bean B,但与bean A属于同一级别的Bean C里面又有bean B,所以就需要一个注册的功能,使用一个容器去存储已经检查过的依赖bean,也就是上面提到的dependentBeanMap

在这里插入图片描述

对应的方法是registerDependentBean

源码如下

	public void registerDependentBean(String beanName, String dependentBeanName) {
		//获取真正的beanName,因为有可能是别名形式
        String canonicalName = canonicalName(beanName);
		//对dependentBeanMap上锁
        //前面判断循环依赖也会对dependBeanMap上锁,这里也会进行上锁
		synchronized (this.dependentBeanMap) {
            //根据beanName去获取该bean的此时的所有依赖bean
            //是一个set集合,并且从这里可以看到,是一个LinkedHashSet
			Set<String> dependentBeans =
					this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
			//往依赖bean的set集合里面去添加所有的
            //如果已经注册过直接return
           
            if (!dependentBeans.add(dependentBeanName)) {
				return;
			}
		}
		//如果没有注册过,注册了进去之后会进行下一个操作
        //给dependenciesForBeanMap进行上锁
        //这个dependenciesForBeanMap其实跟dependentBeanMap有点相反
        //dependenciesForBeanMap存储的是依赖bean与其所有依赖该bean的bean的映射关系
        //而dependentBeanMap存储的是bean与其所有依赖bean的关系
        //相当于是互换的概念,因为bean与依赖bean是多对多的关系
		synchronized (this.dependenciesForBeanMap) {
            //根据依赖bean的beanName去获取所有依赖该bean的bean集合
            //可以看到,也是一个LinkedHashSet
			Set<String> dependenciesForBean =
					this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
            //添加
			dependenciesForBean.add(canonicalName);
		}
	}

在这里插入图片描述
从代码上可以看到,注册依赖beam是要去更新两个容器的

  • dependsBeanMap:bean与所有依赖bean的关系
  • dependenciesForBeanMap:依赖bean与所有依赖该bean的bean的关系
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Spring框架采用依赖注入(DI)的方式来管理Bean,这种方式会解决Bean之间的循环依赖问题。在Spring容器启动时,它会先创建所有的Bean的实例,但是并不会对所有的依赖关系进行完全的填充,而是在构建Bean实例的过程中动态地解决依赖关系,从而避免了循环依赖的问题。 当需要创建一个Bean时,会经过以下步骤: 1. 创建Bean实例:当需要创建一个Bean时,Spring会将其实例化,并创建相应的BeanDefinition对象,以描述Bean的属性、依赖关系等。 2. 填充Bean属性:在Bean实例创建后,Spring会将Bean的属性值注入到Bean实例中。如果某些属性需要其他Bean的引用作为依赖,则Spring会用一个叫做代理的对象替代这些属性,这样就避免了循环依赖。 3. 调用初始化方法:在Bean实例化和属性填充后,Spring会调用Bean的初始化方法(如果有的话)。 对于循环依赖的情况,Spring会在填充属性时采用一些特殊的机制来处理。比如,当第一个Bean需要引用第二个Bean实例时,Spring会先创建一个半成品的Bean实例,然后注入到第一个Bean中。当第一个Bean创建完成后,会将其作为参数传递给第二个Bean的构造函数或者setter方法,以完成第二个Bean的创建。这样,就避免了两个Bean之间的循环依赖问题。 综上所述,Spring框架解决Bean的循环依赖问题的核心思想就是“先创建出半成品的Bean实例,然后在后续的构造器或setter方法中完成Beans之间的注入和填充”。这种方式可以解决循环依赖问题,同时也保证了Bean之间的正确依赖关系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值