- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
3.1.4 注册解析的 BeanDefinition
对于配置文件,解析也解析完了,装饰也装饰完了,对于得到的 beanDinition 已经可以满足后续的使用要求了,唯一还剩下的工作就是注册了,也就是 processBeanDefinition 函数中的 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 代码的解析了。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//使用 beanName 做唯一标识注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
从上面的代码可以看出,解析的 beanDefinition 都会被注册到 BeanDefinitionRegistry 类型的实例 registry 中,而对于 beanDefinition 的注册分成了两部分:通过 beanName 的注册以及通过别名的注册。
1.通过 beanName 注册 BeanDefinition
对于 beanDefinition 的注册,或许很多人认为的方式就是将 beanDefinition 直接放入 map 中就好了,使用 beanName 作为 key。确实,Spring 就是这么做的,只不过除此之外,它还做了点别的事情。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
/*
* 注册前的最后一次校验,这里的校验不同于之前的 XML 文件校验,
* 主要是对于 AbstractBeanDefinition 属性中的 methodOverrides 校验,
* 校验 methodOverrides 是否与工厂方法并存或者 methodOverrides 对应的方法根本不存在
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException (beanDefinition. getResource
Description(), beanName,
"Validation of bean definition failed", ex);
}
}
//因为 beanDefinitionMap 是全局变量,这里定会存在并发访问的情况
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//处理注册已经注册的 beanName 情况
if (oldBeanDefinition != null) {
//如果对应的 BeanName 已经注册且在配置中配置了 bean 不允许被覆盖,则抛出异常。
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition. getResource
Description(), beanName,
"Cannot register bean definition [" + beanDefinition +
"] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with ["
+ beanDefinition + "]");
}
}
}else {
//记录 beanName
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
//注册 beanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
//重置所有 beanName 对应的缓存
resetBeanDefinition(beanName);
}
上面的代码中我们看到,在对于 bean 的注册处理方式上,主要进行了几个步骤。
(1)对 AbstractBeanDefinition 的校验。在解析 XML 文件的时候我们提过校验,但是此校验非彼校验,之前的校验时针对于 XML 格式的校验,而此时的校验时针是对于 AbstractBean Definition 的 methodOverrides 属性的。
(2)对 beanName 已经注册的情况的处理。如果设置了不允许 bean 的覆盖,则需要抛出异常,否则直接覆盖。
(3)加入 map 缓存。
(4)清除解析之前留下的对应 beanName 的缓存。
2.通过别名注册 BeanDefinition
在理解了注册 bean 的原理后,理解注册别名的原理就容易多了。
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
//如果 beanName 与 alias 相同的话不记录 alias,并删除对应的 alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}else {
//如果 alias 不允许被覆盖则抛出异常
if (!allowAliasOverriding()) {
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 + "'.");
}
}
//当 A->B 存在时,若再次出现 A->C->B 时候则会抛出异常
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
由以上代码中可以得知注册 alias 的步骤如下。
(1)alias 与 beanName 相同情况处理。若 alias 与 beanName 并名称相同则不需要处理并删除掉原有 alias。
(2)alias 覆盖处理。若 aliasName 已经使用并已经指向了另一 beanName 则需要用户的设置进行处理。
(3)alias 循环检查。当 A->B 存在时,若再次出现 A->C->B 时候则会抛出异常。
(4)注册 alias。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论