背景简介
出现的原因
我们上面介绍了:
-
AliasRegistry
和SimpleAliasRegistry
他们分别定义和实现了对别名管理的支持 -
SingletonBeanRegistry
定义了对单例 Bean 管理的支持
按照我们之前对 Spring 整合不通接口功能的套路的介绍,现在该有个类来继承SimpleAliasRegistry
同时实现SingletonBeanRegistry
接口,从而实现将"对单例 Bean的管理”、“对别名的管理”两个功能的整合。
职责
将"对单例 Bean的管理”、“对别名的管理”两个功能整合。
注意点
思路很简单,主要关注它里面的实例属性都有什么意义。这在后面的解决循环依赖、进行 bean 回收中都有帮助。
源码
继承关系
1.png实例属性介绍
因为DefaultSingletonBeanRegistry
通过继承SimpleAliasRegistry
直接拥有了对别名的管理功能,所以在DefaultSingletonBeanRegistry
中只需要将SingletonBeanRegistry
的相关功能支持一遍即可。
/**
* Cache of singleton objects: bean name --> bean instance
* 正经的单例 bean 实例缓存,里面放的都是构造、属性填充完成、初始化好的。
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* Cache of singleton factories: bean name --> ObjectFactory
* 单例 bean 工厂的缓存,用于实现懒加载,需要时就拿出来,用 ObjectFactory 创建 Bean 实例并放到
* earlySingletonObjects 等待属性填充和初始化
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* Cache of early singleton objects: bean name --> bean instance
* 用于提前暴露 Bean 实例的地址,用于解决单例 bean 的循环依赖问题,这里面的东西都未执行属性填充和初始化
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
* Set of registered singletons, containing the bean names in registration order
* 注册的单例 Bean 的id集合,等价于 singletonObjects.keySet()
*/
//TODO 没必要专门用个集合来存储,这样维护一致性要花费不少精力
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
/**
* 当前正在创建中的 Bean ,在提供的扩展方法——“通过一个具有创造实例、填充属性、初始化功能的工厂方法
* 直接获得最终单例Bean”中用作记录
*
*/
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* 要排除创建标记的Bean
*/
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* Disposable bean instances: bean name --> disposable instance
*/
// 这里面的bean有删除的钩子,在删除 bean 时,从注册中删除后需要调用这里的方法
// 这里的 disposableBeans 的 key 和要回收的 bean 实例在 singletonObjects 中的 key 一样
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
/**
* Map between containing bean names: bean name --> Set of bean names that the bean contains
*/
// key 对应的 bean 里面有变量是用的 value 中的 bean
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
/**
* Map between dependent bean names: bean name --> Set of dependent bean names
*/
// key 对应的 bean 被 value 中的 bean 依赖,也就是说 value 中的 bean 销毁完 ,key 对应的 bean 才能开始销毁
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/**
* Map between depending bean names: bean name --> Set of bean names for the bean's dependencies
*/
// key 对应的 bean 依赖了 value 中的 bean,也就是说 key 对应的 bean 销毁完,value 中的 bean 才能销毁
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
/**
* List of suppressed Exceptions, available for associating related causes
*/
@Nullable
private Set<Exception> suppressedExceptions;
/**
* 标记值,如果在销毁阶段,不允许新注册单例 Bean
*/
private boolean singletonsCurrentlyInDestruction = false;
实现的接口方法——增
// 将 Bean 实例注册到 singletonObjects 中
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "Bean name must not be null");
Assert.notNull(singletonObject, "Singleton object must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
addSingleton(beanName, singletonObject);
}
}
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
*
* @param beanName the name of the bean
* @param singletonObject the singleton object
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
实现的接口方法——查
@Override
@Nullable
// 根据 bean 的 name(这里指的是 ID ,不是 alias )获得对应的 bean 实例
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
/**
* 返回 id 对应的实例,可能是正经的实例,也可能是为了解决循环依赖提前暴露出的实例地址
* 可以通过入参控制是否在没有得到实例的情况下提前创造实例返回地址
*
* @param beanName the name of the bean to look for
* @param allowEarlyReference 是否创造提前暴露的实例地址
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); // 先看正经缓存中有没有弄好的
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 没拿到,而且正在通过扩展的工厂方法进行获取最终实例属性
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName); // 看工厂方法有没有提前丢到这里
if (singletonObject == null && allowEarlyReference) {// 没有丢这里,看看我们给他创建个实例
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {// 有创建实例的工厂,就搞一下
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
/**
* 正在通过外部工厂方法进行从头到尾的创建
* @param beanName the name of the bean
*/
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
/**
* 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;
}
@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();
}
}
扩展方法——工厂懒加载功能【此工厂仅创建实例】
/**
* 把用来创建 bean 实例的工厂方法保存至 singletonFactories 中
* TODO : 注意了,这个 singletonFactory 仅创建实例,用来解决循环依赖,不负责初始化、填充属性什么的
*
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
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);
}
}
}
扩展方法——完全委托外部完成实例所有创建操作
/**
* Return the (raw) singleton object registered under the given name,
* creating and registering a new one if none registered yet.
*
* @param beanName the name of the bean
* @param singletonFactory the ObjectFactory to lazily create the singleton
* with, if necessary
* @return the registered singleton object
*/
// 创建 beanName 对应的单例 bean ,如果还没创建就创建,创建过就直接返回,
//
// 第二个入参不是懒加载,在这里是直接调用创建然后放到正经缓存中去来。
// 使用表达式入参的原因之一可能是方便用户自定义逻辑,包括使用闭包访问自定义变量什么的。
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 + "'");
}
// 创建前的缓存标记——标记此 beanName 正在使用外部方法创建中
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 创建 bean 实例,并初始化。
// 如果总全局角度来看的话,其实这个里面的逻辑就是调用的 creatBean()
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;
}
// 完成创建,删除标记——将此 beanName 从正在创建的列表中删除
afterSingletonCreation(beanName);
}
if (newSingleton) { // 缓存到正经缓存单例的里面
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
扩展方法——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);
}
}
很容易理解,不在详细介绍,我觉得看实例属性介绍已经够了。
扩展方法——检测依赖是否存在
这个方法常常用来检测循环依赖的存在,其实 Spring 框架是做了针对单例 bean 的解决循环依赖的操作的:通过提前暴露地址解决依赖——这样就不会轮到A—>B—>A时最后回头创建 A,可以直接拿到A实例。
但是上面仅仅是解决属性上的循环依赖,如果连实例都创建不出来,这个循环依赖就没法解决了,所以我们提供一个用来检测依赖的工具函数还有有必要的:
扩展:两种导致无法创建实例的循环依赖
dependOn
,手动配置,A dependOn B
表明B
要在A
之前完整构建完。这种依赖如果出现循环的话,是一个实例都创建不出来的。- 依赖的传递是用来创建实例的构造函数入参
另外,我们在解决循环依赖失败时也要判断影响范围,所以也需要
/**
* 检测依赖
*
* @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);
}
}
// 从 dependentBeanMap 中找,看能否找到从 beanName-->dependentBeanName 的直接/间接映射
// 1. 递归
// 2. 树的深度遍历
// 思路很简单,和 SimpleAliasRegistry 的那个遍历差不多
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
*/
// 判断 beanName 是否被依赖
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);
}
}
扩展方法——清除指定/所有单例 bean
public void destroySingletons() {
if (logger.isDebugEnabled()) {
logger.debug("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
*/
// 这里可以优化吧,这里算作是树形的递归调用,最开始调用起这个函数时也是用的循环,
// 在方法头加一个判断,如果remove发现没有,说明在删除其他单例时就提前删完此单例bean了,直接退出
public void destroySingleton(String beanName) {
// 此函数主要目的是取消单例和对应的 DisposableBean 的注册
// 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) {
// 先将依赖 beanName 的单例销毁才能销毁 beanName
// 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.isDebugEnabled()) {
logger.debug("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
}
for (String dependentBeanName : dependencies) { // 先递归销毁依赖 beanName 的单例
destroySingleton(dependentBeanName);
}
}
// 完成依赖此单例的项目的销毁
// Actually destroy the bean now...
if (bean != null) {
try {
bean.destroy(); // 销毁 beanName
} catch (Throwable ex) {
logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
}
}
// 将包含此单例的项目进行销毁
// TODO 感觉怪怪的,销毁完 beanName 后 containedBeans 应该不能继续工作了,怎么能把这个放在最后面?
// 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);
}
}
// 完成包含此单例的项目的销毁
// 做一些善后操作,beanName 依赖了一些 bean ,把这些映射移除即可
// 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);
}
总结记录
此类主要关注那些实例属性即可,把那些Map
的功能梳理清楚,方法都是见名知意的。
问题
冗余数据结构定义问题
我们在介绍上面的实例属性时很容易发现:我们定义的Map
,Set
那一大串虽然很多很全,但是有点过多了,很多属性我们如果仔细品味的话还是有问题的——有的属性我们觉得一样,实际上可能真的有点冗余;有的属性我们觉得一样,但是他们的职能、使用场景完全不同。
singletonObjects
和registeredSingletons
/**
* Cache of singleton objects: bean name --> bean instance
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* Set of registered singletons, containing the bean names in registration order
*/
//TODO 没必要专门用个集合来存储,这样维护一致性要花费不少精力
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
个人认为没必要专门创建下面的Set
直接用singletonObjects.keySet
足以,原因:
-
registeredSingletons
为私有且未向外暴露,不会出现线程安全之类的其他的的情况,不会拖singletonObjects
的后腿 - 线程的集合类接口,没有理由不用
Bean 销毁的顺序问题
在destroyBean
中,销毁的先后顺序是:
- 依赖此
beanName
的bean
- 此
beanName
对应的bean
- 包含此
beanName
的外部容器
个人感觉应该是3,1,2/1,3,2而不是123。
先销毁了 beanName
对应bean
,外面的容器就无法正常工作了,会造成请求失败。
扩展
冗余数据结构定义问题
dependentBeanMap
和dependenciesForBeanMap
理论上说吧你用逻辑运算也是能算出来这两个是有逻辑重复的。但是如果合并成一个数据结构的话,那就是一个图了,可能要用一个二维矩阵来表示,这需要专门封装很多东西。而且,你获得一个依赖集,遍历那么多,有点复杂了吧。
综上,个人认为这个不能随便替代。
singletonsCurrentlyInCreation
和earlySingletonObjects
、singletonFactories
singletonsCurrentlyInCreation
这个钩子是专门为扩展方法中的那个将 Bean 的所有实例化、初始化都委托给外部函数时用的记录位置。
earlySingletonObjects
和singletonFactories
是一起用的,用来解决单例 bean 的循环依赖问题。一般在上面的"将 Bean 的所有实例化、初始化都委托给外部函数"中的外部函数中自行使用。
线程安全问题
只有singletonObjects
采用了线程安全的集合,此类的复杂操作时都自行使用了隐式锁。这点做的很全面。【在个别方法没有用锁的(例如isDependent
)是因为用private
,在调用它的public
中加了锁。】
因为singletonObjects
要暴露出去,所以采用线程安全的集合也是完全可以理解的。
单例 Bean 的循环依赖问题
一般,我们在进行单例 Bean 的生成和注册时都是用的提供的扩展方法,传入一个创建、初始化的方法来做。在这种情况下,一个单例 Bean 的流转顺序我们在DefaultSingletonRegistry
的角度看大概是这样的:
感觉自己时序图画的完全不符合规范。
主要目的是说明在各个缓存块之间的流转关系,这个后面想个好点的、更直观的表达方式吧。
网友评论