spring源码阅读2-2——bean的管理
我们在《spring源码阅读2-1——bean的管理》中,摸清了BeanFactory家族的整体框架和功能概览,本文将继续剖析spring如何将BeanFactory一点一点实现的。
首先是上次已经获得的BeanFactory家族类图,本人在这上方加了备注,希望能让大家看的更为清晰。
BeanFactory家族类图
SimpleAliasRegistry
这个类实现了AliasRegistry接口
alias注册管理首先是registerAlias()
方法的实现:
为了方便读者阅读,加入了中文注释
源码:
private final Map<String, String> aliasMap = new ConcurrentHashMap<String, String>(16);
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty"); //参数校验
Assert.hasText(alias, "'alias' must not be empty");
if (alias.equals(name)) { //保障了不存在与正名相同的别名
this.aliasMap.remove(alias);
}
else {
if (!allowAliasOverriding()) { //是否开启覆盖注册,默认为true
String registeredName = this.aliasMap.get(alias);
if (registeredName != null && !registeredName.equals(name)) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
checkForAliasCircle(name, alias); //确保添加的没有name和alias值相反的数据且alias和name不相等
this.aliasMap.put(alias, name);
}
}
---------------------------- 方法封装 ------------------------------------------------------
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
protected void checkForAliasCircle(String name, String alias) {
if (alias.equals(canonicalName(name))) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
解读:
第一句是声明了一个线程安全的散列表对象(ConcurrentHashMap)来作为缓存(如果对线程安全和非线程安全的集合类不太清楚,建议可以系统地去学习)来存放注册的alias。
aliasMap以alias为键,name为值,其中alias为别名,而name是正名,如下图所示。
从代码中我们看到我们可以为把alias当做name来再次注册别名,举个例子,叫"张三"的人有两个外号:"小张"和"小三",那我可以为小三注册别名,叫"三哥",即name="小三", alias="三哥"(当然name="张三"也是可以的)。但是不能注册name和alias正好相反的数据,以及name和alias相等的数据。为什么这么设计呢?后面会给出解释。
源码:
@Override
public void removeAlias(String alias) {
String name = this.aliasMap.remove(alias);
if (name == null) {
throw new IllegalStateException("No alias '" + alias + "' registered");
}
}
@Override
public boolean isAlias(String name) {
return this.aliasMap.containsKey(name);
}
@Override
public String[] getAliases(String name) {
List<String> result = new ArrayList<String>();
synchronized (this.aliasMap) {
retrieveAliases(name, result);
}
return StringUtils.toStringArray(result);
}
//遍历散列表,获取name为定值的所有alias
private void retrieveAliases(String name, List<String> result) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String alias = entry.getKey();
result.add(alias);
retrieveAliases(alias, result); //注意是有回调的哦
}
}
}
解读:
removeAlias()
和isAlias()
就不说了,根据name来获取alias的方法getAliases(String name)
,由于散列表是以alias为键,而name为值,要想找出满足条件的所有alias值,就必须对散列表进行遍历,找出所有name和给定值相等的值。
我在注释中注明了是有回调函数存在的,当找到name值和给定值相等是,会把这个alias作为name,再次对散列表进行遍历,如下图所示
返回值中有"三哥",而三哥对应的name是"小三"。这也是在注册时限制注册的数据name和alias不能正好相反且name和alias不能相等的原因,否则回调函数将陷入无限循环中去。
因此,在设计阶段和编程过程中,如果涉及回调,就要规避无限循环的可能。
至此,我们已经阅读完了这个类的所有方法(还有一个resolveAliases()入参是一个较为复杂的对象,暂时不解释,等下回碰到了再细究)
DefautlSingletonBeanRegistry
该类继承了SimpleAliasRegistry,并且实现SingletonBeanRegistry接口。
同样的,来看下是如何实现registerSingleton(String beanName, Object singletonObject)
方法:
源码:
protected static final Object NULL_OBJECT = new Object();
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "'beanName' 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);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
解读:
同SimpleAliasRegistry,这里也是用散列表来做缓存。但是SingleBean的注册要来的复杂,因为bean还涉及到初始化的问题,因此这里有多个缓存用的对象:
- singletonObjects是一个线程安全的散列表,用于存放已注册的SingleBean实例
- singletonFactories用来存放初始化SingleBean实例的factory对象
- earlySingletonObjects 用来存放已存在但是未注册的SingleBean实例
- registeredSingletons是一个集合,存放的是已注册的SingleBean对象的名称
从代码中可以看到,当一个Bean被注册后,就会将这个Bean实例添加到缓存中去,如果这个实例存在对应的BeanFactory,则BeanFactory将被移除,或者这个Bean存在于earlySingletonObjects中,也将从earlySingletonObjects中移除。
这个过程可能不太好理解,举个例子来说明吧:
有a,b,c三个学生要在大学注册,a社会自考招生,没有学籍;b是高中FactoryB的学生;c是该大学的留级生,学籍已存在。b注册完成后,要将学籍档案转移到大学中,c也要将学籍档案转移到当前年级的档案库中去(如下图所示)
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
@Override
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
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 != NULL_OBJECT ? singletonObject : null);
}
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
解读:
这段逻辑比较清晰了,还是那上面那个例子来讲,要获得一个学生的学籍档案,首先直接从该年级的学籍档案中查看,如果没有,这个学生是否为该入学注册的学生(isSingletonCurrentlyIncreation()
方法判断),如果是,就依次查找earlySingletonObjects和singletonFactories对象。
再看源码的过程中,会有很多疑惑,比如这里为什么要设置这么多的缓存对象,想alias那样注册不就好了,为什么要那么麻烦呢?但是,要沉住气,坚持下去,最终会让你豁然开朗。这也许是源码阅读的魅力所在吧
源码:
@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();
}
}
这几个方法的重写就不说了,和alias的注册管理是一样的。
源码:
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
public void registerDependentBean(String beanName, String dependentBeanName) {
// A quick check for an existing entry upfront, avoiding synchronization...
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {
return;
}
// No entry yet -> fully synchronized manipulation of the dependentBeans Set
synchronized (this.dependentBeanMap) {
dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
dependentBeans = new LinkedHashSet<String>(8);
this.dependentBeanMap.put(canonicalName, dependentBeans);
}
dependentBeans.add(dependentBeanName);
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
if (dependenciesForBean == null) {
dependenciesForBean = new LinkedHashSet<String>(8);
this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
}
dependenciesForBean.add(canonicalName);
}
}
protected boolean isDependent(String beanName, String dependentBeanName) {
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 (isDependent(transitiveDependency, dependentBeanName)) {
return true;
}
}
return false;
}
protected boolean hasDependentBean(String beanName) {
return this.dependentBeanMap.containsKey(beanName);
}
public String[] getDependentBeans(String beanName) {
Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
if (dependentBeans == null) {
return new String[0];
}
return StringUtils.toStringArray(dependentBeans);
}
解读:
DefaultSingletonBeanRegistry不仅实现了SingleBeanRegistry中的对SingleBean注册管理的方法,还扩展了对于SingleBean的依赖关系进行管理。
缓存对象:
- dependentBeanMap 存储bean所依赖的其他bean组件
- dependenciesForBeanMap 存储当前bean被哪些其他bean组件依赖
方法:
- registerDependentBean() ==> 注册bean所依赖的dependentBean
- isDependent() ==> 返回denpendentBean是否为bean的依赖bean
- hasDenpendentBean() ==> 返回bean是否有dependentBean
- getDependentBeans() ==> 返回bean的所有dependentBean
- destroyBean() ==> 删除一个bean,删除其依赖信息
总结
总结.png本文到这就要结束了,回顾下
本文主要对Factory家族中的SimpleAliasRegistry和DefaultSingletonBeanRegistry进行分析。
其主要通过线程安全的散列表做缓存,实现了alias管理、SingleBean管理以及Bean依赖的管理的功能。
本文到此就结束,希望能够帮到大家。
纯属原创,转载请声明出处。
——作者:陈来件(QQ:810522442)