在上一篇《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)