Spring5源码浅析(七)—DefaultSingletonBeanRegistry(下)

本文深入分析了Spring框架中DefaultSingletonBeanRegistry类的实现细节,包括Bean的注册、销毁流程,以及依赖关系的管理和清理过程。特别探讨了如何处理循环依赖和Bean的生命周期管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       在上一篇《Spring5源码浅析(七)—DefaultSingletonBeanRegistry(上)》中,给大家分析了DefaultSingletonBeanRegistry中的registerSingleton,addSingleton,addSingletonFactory这三个用于单例Bean的注册管理的函数,以及getSingleton及其另外两个重载这几个用于单例Bean的获取函数,另外我们也分析了Spring是如何处理循环依赖问题的,以及为什么Spring无法处理构造函数的循环依赖问题.本次呢,我们继续来看DefaultSingletonBeanRegistry中剩下的几个函数.

       首先是SingletonBeanRegistry接口剩余的以下几个函数:

    @Override
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }
    @Override
    public String[] getSingletonNames() {
        synchronized (this.singletonObjects) {
            return StringUtils.toStringArray(this.registeredSingletons);
        }
    }
    @Override
    public int getSingletonCount() {
        synchronized (this.singletonObjects) {
            return this.registeredSingletons.size();
        }
    }
    /**
     * Exposes the singleton mutex to subclasses and external collaborators.
     * <p>Subclasses should synchronize on the given Object if they perform
     * any sort of extended singleton creation phase. In particular, subclasses
     * should <i>not</i> have their own mutexes involved in singleton creation,
     * to avoid the potential for deadlocks in lazy-init situations.
     */
    public final Object getSingletonMutex() {
        return this.singletonObjects;
    }

       我们在上一篇《Spring5源码浅析(七)—DefaultSingletonBeanRegistry(上)》提到了无论是普通的单例对象注册(注册进入singletonObjects集合,且与工厂型单例互斥),还是工厂型的单例对象注册(注册进入singletonFactories,且与普通的单例对象互斥),这两者都会在registeredSingletons进行登记,然后在这里就可以看到getSingletonNames()以及getSingletonCount()这两个函数都给的是registeredSingletons集合里的信息.

      其次,containsSingleton则使用的是singletonObjects这个集合.

        最后,getSingletonMutex这个方法,则采用了final的修饰符,也就意味着,这个方法可以被继承,但不能够被子类修改.因为他是把所有的singletonObjects对象给返回了,这就保证了所有的子类以及外部调用者都共用一个singleton容器,避免了懒加载情况下潜在的死锁问题.

       在这个实现类中,除了提供SingletonBeanRegistry接口的方法外,它还额外提供了一些公开函数,我们现在可以来看一下:

/**
	 * Remove the bean with the given name from the singleton cache of this factory,
	 * to be able to clean up eager registration of a singleton if creation failed.
	 * @param beanName the name of the bean
	 * @see #getSingletonMutex()
	 */
	protected void removeSingleton(String beanName) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.remove(beanName);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.remove(beanName);
		}
	}

    public void setCurrentlyInCreation(String beanName, boolean inCreation) {
		Assert.notNull(beanName, "Bean name must not be null");
		if (!inCreation) {
			this.inCreationCheckExclusions.add(beanName);
		}
		else {
			this.inCreationCheckExclusions.remove(beanName);
		}
	}

	public boolean isCurrentlyInCreation(String beanName) {
		Assert.notNull(beanName, "Bean name must not be null");
		return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
	}

	protected boolean isActuallyInCreation(String beanName) {
		return isSingletonCurrentlyInCreation(beanName);
	}

	/**
	 * Return whether the specified singleton bean is currently in creation
	 * (within the entire factory).
	 * @param beanName the name of the bean
	 */
	public boolean isSingletonCurrentlyInCreation(String beanName) {
		return this.singletonsCurrentlyInCreation.contains(beanName);
	}

       首先是removeSingleton,这个函数处理的是如果一个Bean在创建失败的时候,可以清除这个Bean的残余信息.

       紧接着是setCurrentlyInCreation与isCurrentlyInCreation这一组函数,setCurrentlyInCreation函数先将正在创建中的bean从inCreationCheckExclusions集合中移除,而把未处于创建状态的bean添加到inCreationCheckExclusions集合中.然后我们可以看到Spring在isCurrentlyInCreation函数中用来判断一个Bean是否处于创建中的方式就是首先检查这个Bean在inCreationCheckExclusions集合中是否存在,如果不存在,其次则是调用isActuallyInCreation来判断这个Bean是否存在于singletonsCurrentlyInCreation这个集合中.

       最后是isActuallyInCreation和isSingletonCurrentlyInCreation这两个函数,这两个其实是一个函数,主要用于判断给定的Bean是否存在于singletonsCurrentlyInCreation集合中.

       现在呢,我们来看第二组:

    /**
	 * Add the given bean to the list of disposable beans in this registry.
	 * <p>Disposable beans usually correspond to registered singletons,
	 * matching the bean name but potentially being a different instance
	 * (for example, a DisposableBean adapter for a singleton that does not
	 * naturally implement Spring's DisposableBean interface).
	 * @param beanName the name of the bean
	 * @param bean the bean instance
	 */
	public void registerDisposableBean(String beanName, DisposableBean bean) {
		synchronized (this.disposableBeans) {
			this.disposableBeans.put(beanName, bean);
		}
	}

	/**
	 * Register a containment relationship between two beans,
	 * e.g. between an inner bean and its containing outer bean.
	 * <p>Also registers the containing bean as dependent on the contained bean
	 * in terms of destruction order.
	 * @param containedBeanName the name of the contained (inner) bean
	 * @param containingBeanName the name of the containing (outer) bean
	 * @see #registerDependentBean
	 */
	public void registerContainedBean(String containedBeanName, String containingBeanName) {
		synchronized (this.containedBeanMap) {
			Set<String> containedBeans =
					this.containedBeanMap.computeIfAbsent(containingBeanName, k -> new LinkedHashSet<>(8));
			if (!containedBeans.add(containedBeanName)) {
				return;
			}
		}
		registerDependentBean(containedBeanName, containingBeanName);
	}

	/**
	 * Register a dependent bean for the given bean,
	 * to be destroyed before the given bean is destroyed.
	 * @param beanName the name of the bean
	 * @param dependentBeanName the name of the dependent bean
	 */
	public void registerDependentBean(String beanName, String dependentBeanName) {
		String canonicalName = canonicalName(beanName);

		synchronized (this.dependentBeanMap) {
			Set<String> dependentBeans =
					this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
			if (!dependentBeans.add(dependentBeanName)) {
				return;
			}
		}

		synchronized (this.dependenciesForBeanMap) {
			Set<String> dependenciesForBean =
					this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
			dependenciesForBean.add(canonicalName);
		}
	}

	/**
	 * Determine whether the specified dependent bean has been registered as
	 * dependent on the given bean or on any of its transitive dependencies.
	 * @param beanName the name of the bean to check
	 * @param dependentBeanName the name of the dependent bean
	 * @since 4.0
	 */
	protected boolean isDependent(String beanName, String dependentBeanName) {
		synchronized (this.dependentBeanMap) {
			return isDependent(beanName, dependentBeanName, null);
		}
	}

	private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
		if (alreadySeen != null && alreadySeen.contains(beanName)) {
			return false;
		}
		String canonicalName = canonicalName(beanName);
		Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
		if (dependentBeans == null) {
			return false;
		}
		if (dependentBeans.contains(dependentBeanName)) {
			return true;
		}
		for (String transitiveDependency : dependentBeans) {
			if (alreadySeen == null) {
				alreadySeen = new HashSet<>();
			}
			alreadySeen.add(beanName);
			if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Determine whether a dependent bean has been registered for the given name.
	 * @param beanName the name of the bean to check
	 */
	protected boolean hasDependentBean(String beanName) {
		return this.dependentBeanMap.containsKey(beanName);
	}

	/**
	 * Return the names of all beans which depend on the specified bean, if any.
	 * @param beanName the name of the bean
	 * @return the array of dependent bean names, or an empty array if none
	 */
	public String[] getDependentBeans(String beanName) {
		Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
		if (dependentBeans == null) {
			return new String[0];
		}
		synchronized (this.dependentBeanMap) {
			return StringUtils.toStringArray(dependentBeans);
		}
	}

	/**
	 * Return the names of all beans that the specified bean depends on, if any.
	 * @param beanName the name of the bean
	 * @return the array of names of beans which the bean depends on,
	 * or an empty array if none
	 */
	public String[] getDependenciesForBean(String beanName) {
		Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(beanName);
		if (dependenciesForBean == null) {
			return new String[0];
		}
		synchronized (this.dependenciesForBeanMap) {
			return StringUtils.toStringArray(dependenciesForBean);
		}
	}

       在这组函数中,首先引入了DisposableBean的概念,DisposableBean是一个函数式接口,里面只有一个destroy函数,对于那些想要在销毁时释放资源的Bean来说,就需要实现这个接口.BeanFactory将会在一个Scope Bean的个别销毁上调用该destroy函数.所以我们就很清楚了,这里的registerDisposableBean函数就是为了给Bean注册销毁方法的,一般来说,只要注册了一个singleton,就需要相应的注册一个销毁函数,并且Spring将Bean对应的销毁方法做了一个单独的存储,放在了disposableBeans这个集合中.

       其次,registerContainedBean这个函数关注的是Bean之间的关系,他注册了两个Bean之间的包含关系(例如内嵌Bean和它的外部容器Bean之间),并且就销毁顺序来说,Containing(outer) Bean依赖于Contained(inner) Bean。这里使用了containedBeanMap集合来存储外部类与内部类的对应关系,并且因为一个外部类可以对应多个内部类,所以在containedBeanMap集合中外部类的名字是key,而存储内部类的value则是一个LinkedHashSet.在registerContainedBean这个函数中,首先使用了Map的computeIfAbsent()(computeIfAbsent是Java1.8开始提供的),来判断containedBeanMap集合中是否已经有containedBeanName对应的记录了,如果有,就返回它对应的LinkedHashSet,如果没有,就返回一个新的LinkedHashSet。然后将内部类添加到这个LinkedHashSet中,如果containedBeanName对应的LinkedHashSet中已经有当前指定的这个内部类了,就直接结束操作,如果还没有,就在将内部类添加进LinkedHashSet集合中后,调用registerDependentBean函数来注册containedBeanName与containingBeanName的依赖关系.

       registerDependentBean用于为指定的Bean注册一个依赖Bean,这个依赖Bean的主要作用是在给定的Bean被销毁之前要能被销毁.而在registerDependentBean函数中,他首先调用了父类SimpleAliasRegistry中的canonicalName()函数来获取给定Bean的真实名字(关于canonicalName方法的详细介绍可参考《Spring5源码浅析(六)—SimpleAliasRegistry》),其次,使用dependentBeanMap来存储那些依赖于指定Bean的Bean的名称.最后,又使用dependenciesForBeanMap来进行反向存储(与dependentBeanMap相反),这两次操作的方式与registerContainedBean函数里的操作方式相似.

       在依赖关系注册完成之后,紧接着的一组就是依赖关系的判断及获取.首先是Spring4.0开始提供了一个isDependent函数,在这个函数里,首先也是调用了父类SimpleAliasRegistry的canonicalName来获取指定Bean的真实名字(排除别名),然后从dependentBeanMap集合中获取依赖于指定Bean的所有Bean的名字,当然通过上面的学习,我们知道这些Bean的名字是存放在LinkedHashSet中的.然后判断这个集合中是否包含dependentBeanName,如果不包含,Spring又继续往下走开始处理传递依赖的情况,当然,这里采取的是递归策略.

       hasDependentBean函数主要用于判断是否有Bean依赖于给定的Bean,是通过给定的Bean是否在dependentBeanMap中进行注册过来进行判断的.

       getDependentBeans则是为了获取依赖于该Bean的所有的Bean名称,如果没有,就返回一个空的字符串数组.getDependenciesForBean则是为了获取指定的Bean所依赖的所有的Bean的名称,如果没有,就返回一个空的字符串数组.

      这两个函数是从dependentBeanMap/dependenciesForBeanMap中进行获取,并调用了StringUtils.toStringArray函数.

      DefaultSingletonBeanRegistry这个类中的最后一组是关于Bean销毁的处理的,如下所示:

    public void destroySingletons() {
		if (logger.isTraceEnabled()) {
			logger.trace("Destroying singletons in " + this);
		}
		synchronized (this.singletonObjects) {
			this.singletonsCurrentlyInDestruction = true;
		}

		String[] disposableBeanNames;
		synchronized (this.disposableBeans) {
			disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
		}
		for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
			destroySingleton(disposableBeanNames[i]);
		}

		this.containedBeanMap.clear();
		this.dependentBeanMap.clear();
		this.dependenciesForBeanMap.clear();

		clearSingletonCache();
	}

	/**
	 * Clear all cached singleton instances in this registry.
	 * @since 4.3.15
	 */
	protected void clearSingletonCache() {
		synchronized (this.singletonObjects) {
			this.singletonObjects.clear();
			this.singletonFactories.clear();
			this.earlySingletonObjects.clear();
			this.registeredSingletons.clear();
			this.singletonsCurrentlyInDestruction = false;
		}
	}

	/**
	 * Destroy the given bean. Delegates to {@code destroyBean}
	 * if a corresponding disposable bean instance is found.
	 * @param beanName the name of the bean
	 * @see #destroyBean
	 */
	public void destroySingleton(String beanName) {
		// Remove a registered singleton of the given name, if any.
		removeSingleton(beanName);

		// Destroy the corresponding DisposableBean instance.
		DisposableBean disposableBean;
		synchronized (this.disposableBeans) {
			disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
		}
		destroyBean(beanName, disposableBean);
	}

	/**
	 * Destroy the given bean. Must destroy beans that depend on the given
	 * bean before the bean itself. Should not throw any exceptions.
	 * @param beanName the name of the bean
	 * @param bean the bean instance to destroy
	 */
	protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
		// Trigger destruction of dependent beans first...
		Set<String> dependencies;
		synchronized (this.dependentBeanMap) {
			// Within full synchronization in order to guarantee a disconnected Set
			dependencies = this.dependentBeanMap.remove(beanName);
		}
		if (dependencies != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
			}
			for (String dependentBeanName : dependencies) {
				destroySingleton(dependentBeanName);
			}
		}

		// Actually destroy the bean now...
		if (bean != null) {
			try {
				bean.destroy();
			}
			catch (Throwable ex) {
				if (logger.isInfoEnabled()) {
					logger.info("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
				}
			}
		}

		// Trigger destruction of contained beans...
		Set<String> containedBeans;
		synchronized (this.containedBeanMap) {
			// Within full synchronization in order to guarantee a disconnected Set
			containedBeans = this.containedBeanMap.remove(beanName);
		}
		if (containedBeans != null) {
			for (String containedBeanName : containedBeans) {
				destroySingleton(containedBeanName);
			}
		}

		// Remove destroyed bean from other beans' dependencies.
		synchronized (this.dependentBeanMap) {
			for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
				Map.Entry<String, Set<String>> entry = it.next();
				Set<String> dependenciesToClean = entry.getValue();
				dependenciesToClean.remove(beanName);
				if (dependenciesToClean.isEmpty()) {
					it.remove();
				}
			}
		}

		// Remove destroyed bean's prepared dependency information.
		this.dependenciesForBeanMap.remove(beanName);
	}

        destroySingletons()函数首先拿到了所有实现了DisposableBean接口的所有Bean的名字,其次开始为遍历这些Bean,为它们调用destroySingleton函数,然后就是对依赖关系的清除,最后则是调用clearSingletonCache函数来清空单例缓存.

        在clearSingletonCache这个函数中,主要是清空了以下四个集合的数据:singletonObjects,singletonFactories,earlySingletonObjects,registeredSingletons这些集合的数据,将这四个集合还原为了初始化状态,并且将singletonsCurrentlyInDestruction这个变量置为false.

         我们现在来看destroySingleton这个函数,在destroySingletons中循环调用的就是这个函数.在这个函数中,Spring首先调用了removeSingleton,来移除singletonObjects,singletonFactories,earlySingletonObjects以及registeredSingletons这几个集合中的Bean记录.其次因为要销毁这个bean,所以他紧接着就将该Bean对应的DisposableBean从disposableBeans中移除掉了,并在移除之后,调用了destroyBean这个函数,来进行Bean的销毁.

       最终,destroyBean来执行真正的销毁操作:1.在这里,Spring考虑了依存关系,因此在销毁当前这个Bean之前,他要把所有依赖于指定Bean的Bean先行销毁,因此他从dependentBeanMap中移除并取出了依赖于指定Bean的所有Bean所在的集合,如果这个集合不为空,就遍历这个集合,并为集合中的Bean调用destroySingleton;2.在消除了依赖于指定Bean的Bean之后,就开始调用指定Bean的destroy函数(存放于DisposableBean中)来执行销毁操作.3.在个体的Bean销毁之后,Spring开始清除该Bean的所有内嵌Bean。4.遍历dependentBeanMap,如果其他Bean与指定Bean有依赖关系,则移除依赖关系记录.(他依赖于其他Bean,因此需要从其他Bean中移除这条关系).5.最后,将他所存储的与其他Bean的依赖关系进行移除.

       截止到这里,我们已经分析了DefaultSingletonBeanRegistry中的所有函数,及其集合,整体回顾,会发现DefaultSingletonBeanRegistry主要侧重于Bean的注册,销毁,以及依赖关系(关联关系)的注册和销毁,可能到这个层面上,仍然是比较偏底层,也因此会比较陌生。之后,我们会沿着继承线继续往上移,因此将会分享DefaultSingletonBeanRegistry的子类FactoryBeanRegistrySupport这个类。

      整体来看,这里有几个类里还有几处编程的小技巧,首先是关于异常的处理,尤其是在getSingleton这个函数中,把异常都存放到了一个集合中,并且使用的是自定义异常.其次是支持多线程并发情况的集合的使用(e.g.ConcurrentHashMap),最后是对应关系的双向存储,以及对Bean的生命周期的细分.

      如果你在文中发现有什么问题,或者有什么其他的建议,欢迎留言或email(jacob_earl@163.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值