前言
在 Spring IoC:parseDefaultElement详解(上)中,我们介绍了 BeanDefinition 的解析过程。本文将介绍 BeanDefinition 的注册。
processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1.进行元素解析, 经过这个方法后,bdHolder已经包含我们配置文件中配置的各种属性了,例如name、class、id、alias之类的属性
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2.若存在默认标签的子节点下再有自定义属性,需要再次对自定义标签再进行解析(基本不用,不做深入解析)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 3.解析完成后,需要对解析后的bdHolder进行注册,注册操作委托给BeanDefinitionReaderUtils的registerBeanDefinition方法
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 4.最后发出响应事件,通知相关的监听器,这个bean已经加载完成了
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
Spring IoC:parseDefaultElement详解(上)中解析了 delegate.parseBeanDefinitionElement(ele) 方法,本文将解析 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 方法。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 1.拿到beanName
String beanName = definitionHolder.getBeanName();
// 2.注册beanName、BeanDefinition到缓存中(核心逻辑),实现类为; DefaultListableBeanFactory
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 注册bean名称的别名(如果有的话)
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 3.注册bean的beanName和对应的别名映射到缓存中(缓存:aliasMap)
registry.registerAlias(beanName, alias);
}
}
}
1.首先拿到 beanName。
2.注册 beanName、BeanDefinition 到缓存中,见代码块1详解。
3.如果有别名,则注册 bean 的 beanName 和对应的别名映射到 aliasMap 缓存中,见代码块3详解。
代码块1:registerBeanDefinition(beanName, definitionHolder.getBeanDefinition())
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 1.beanName和beanDefinition为空校验
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 注册前的最后校验
((AbstractBeanDefinition) beanDefinition).validate();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
// 首先根据beanName从beanDefinitionMap缓存中尝试获取
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
// 2.beanName存在于缓存中
if (!isAllowBeanDefinitionOverriding()) {
// 如果不允许相同beanName重新注册,则直接抛出异常
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
} else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
} else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 将本次传进来的beanName 和 BeanDefinition映射放入beanDefinitionMap缓存(以供后续创建bean时使用)
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
// 3.beanName不存在于缓存中
if (hasBeanCreationStarted()) {
// 3.1 bean创建阶段已经开始
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 将本次传进来的beanName 和 BeanDefinition映射放入beanDefinitionMap缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
// 将本次传进来的beanName 加入beanDefinitionNames缓存
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 将beanName从manualSingletonNames缓存移除
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
} else {
// 3.2 bean创建阶段还未开始
// Still in startup registration phase
// 将本次传进来的beanName 和 BeanDefinition映射放入beanDefinitionMap缓存
this.beanDefinitionMap.put(beanName, beanDefinition);
// 将本次传进来的beanName 加入beanDefinitionNames缓存
this.beanDefinitionNames.add(beanName);
// 将beanName从manualSingletonNames缓存移除
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 4.如果存在相同beanName的BeanDefinition,并且beanName已经存在单例对象,则将该beanName对应的缓存信息、单例对象清除,
// 因为这些对象都是通过oldBeanDefinition创建出来的,需要被覆盖掉的,
// 我们需要用新的BeanDefinition(也就是本次传进来的beanDefinition)来创建这些缓存和单例对象
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
这个方法会将 beanName 添加到 beanDefinitionNames 缓存,将 beanName 和 BeanDefinition 的映射关系添加到beanDefinitionMap 缓存。
如果 beanName不重复(一般不会重复),对于我们当前正在解析的 obtainFreshBeanFactory 方法来说,因为 bean 创建还未开始,因此会走到 3.2 进行缓存的注册。
另外,如果 beanName 重复,并且该 beanName 已经存在单例对象,则会调用 resetBeanDefinition 方法,见代码块2详解。
代码块2:resetBeanDefinition(beanName)
protected void resetBeanDefinition(String beanName) {
// Remove the merged bean definition for the given bean, if already created.
// 1.删除beanName的mergedBeanDefinitions缓存(如果有的话)
clearMergedBeanDefinition(beanName);
// Remove corresponding bean from singleton cache, if any. Shouldn't usually
// be necessary, rather just meant for overriding a context's default beans
// (e.g. the default StaticMessageSource in a StaticApplicationContext).
// 2.从单例缓存中删除该beanName对应的bean(如果有的话)
destroySingleton(beanName);
// Reset all bean definitions that have the given bean as parent (recursively).
// 3.重置beanName的所有子Bean定义(递归)
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
// 当前遍历的BeanDefinition的parentName为beanName,则递归调用resetBeanDefinition进行重置
if (beanName.equals(bd.getParentName())) {
resetBeanDefinition(bdName);
}
}
}
}
比较简单,将该 beanName 的 mergedBeanDefinitions 缓存信息删除、单例缓存删除。如果存在子Bean定义,则递归重置。实际开发过程中,基本不会出现 beanName 相同的情况,因此基本不会走到该方法。
代码块3:registry.registerAlias(beanName, alias)
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 1.如果别名和beanName相同,则不算别名,从aliasMap缓存中移除
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
// 2.如果别名已经注册过,直接返回
return;
}
// 3.如果存在相同的别名,并且不允许别名覆盖,则抛出异常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 4.检查 name和alias是否存在循环引用。例如A的别名为B,B的别名为A
checkForAliasCircle(name, alias);
// 5.将别名和beanName的映射放到aliasMap缓存中
this.aliasMap.put(alias, name);
}
}
将别名和 beanName 注册到 aliasMap 缓存。
总结
至此,parseDefaultElement 方法解析结束。我们通过 Spring IoC:parseDefaultElement详解(上)和本文共两篇文章来对 parseDefaultElement 方法进行解析。简单来说,就是将 Document 里的 Bean 节点内容解析成 BeanDefinition,然后将 BeanDefinition 注册到缓存中。
执行完 parseDefaultElement 方法,我们得到了两个重要的缓存:
- beanDefinitionNames 缓存。
- beanDefinitionMap 缓存。
这两个缓存在之后的 IoC 构建过程中会发挥很重要的作用,大家在这边先有个印象。