Spring-AliasRegistry
使用Spring 的时候我们可以很容易的为某个bean 配置一个或多个别名
<bean id="app:dataSource" class="...">
<alias name="app:dataSoure" alias="user:dataSoure"/>
<alias name="app:dataSoure" alias="device:dataSoure"/>
</bean>
或者: 直接使用bean标签的name属性,就是别名 <bean id="aaa",name="bbb,ccc,ddd"/>
使用 @Bean 注解的时候
@Bean(value = {"aaa", "bbb", "ccc"})
那么 除了第一个是这个 beanName(bean id) 之外、其他的都是 alias
public interface AliasRegistry {
/**
* 为这个 name 注册一个 alias
*/
void registerAlias(String name, String alias);
/**
* 从注册表中移除这个alias对应的关系
*/
void removeAlias(String alias);
/**
* 给定的这个 name是否是一个 别名
*/
boolean isAlias(String name);
/**
* 根据这个 bean name 获取所有他的别名
*/
String[] getAliases(String name);
}
AliasRegistry 其中的一个实现类 SimpleAliasRegistry
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
使用 Map 来存储、key 为 alias 、value 为 beanName (并不是意味着这个value只能是在spring 容器中存在的bean 的 id、也可以是一个 alias、比如说我对象A有个别名是小A、那么这个小A同样可以有它的别名小AA、那么Map的情况就是[(小A,对象A的id/beanName),(小AA,小A)] )
@Override
public void removeAlias(String alias) {
synchronized (this.aliasMap) {
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<>();
synchronized (this.aliasMap) {
retrieveAliases(name, result);
}
return StringUtils.toStringArray(result);
}
/**
* Transitively retrieve all aliases for the given name.
*
* @param name the target name to find aliases for---bean name
* @param result the resulting aliases list
*/
private void retrieveAliases(String name, List<String> result) {
this.aliasMap.forEach((alias, registeredName) -> {
if (registeredName.equals(name)) {
result.add(alias);
retrieveAliases(alias, result);
}
});
}
上面的方法实现相对而已是比较简单的
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 保证以下的操作都是原子性的、如果并发注册的话就会存在 循环引用的问题
synchronized (this.aliasMap) {
if (alias.equals(name)) {
// 这一步是有必要这么做的、如果这个 alias 已经被其他的 bean 所使用、
// 那么我这个算是最新的了
// 至于为啥不放在 map 里面、因为key 和 value 一样的话、
// 后面的 getAliases 会死循环
this.aliasMap.remove(alias);
} else {
// 根据这个 alias 找出是否已经注册的
String registeredName = this.aliasMap.get(alias);
// 已经有人注册过这个 alias了
if (registeredName != null) {
// 那么巧、bean Name 是一样的、
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 是否允许 alias覆盖、默认是允许的
if (!allowAliasOverriding()) {
throw new IllegalStateException("xxx 已省略显示");
}
}
// 检查是否循环依赖
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
}
关于 alias的循环注册
protected void checkForAliasCircle(String name, String alias) {
// 我们要注册的是 name 拥有别名 alias
// 那么我们就要判断是否 有 alias 拥有别名 name 、
// 如果有的话、那么就是循环依赖了
if (hasAlias(alias, name)) {
throw new IllegalStateException("省略....");
}
}
假如我们已经有了 test拥有别名testAlias01 的关系、那么我们现在想要注册 testAlias01 拥有 别名test
这个关系、那么就检查、看看再已用的关系中是否已经有 test拥有别名testAlias01 关系、如果有则是 别名的循环依赖
A->B->C->D->A
这种也是循环
Spring 源码对应的单元测试
class SimpleAliasRegistryTests {
@Test
void aliasChaining() {
SimpleAliasRegistry registry = new SimpleAliasRegistry();
registry.registerAlias("test", "testAlias");
registry.registerAlias("testAlias", "testAlias2");
registry.registerAlias("testAlias2", "testAlias3");
assertThat(registry.hasAlias("test", "testAlias")).isTrue();
assertThat(registry.hasAlias("test", "testAlias2")).isTrue();
assertThat(registry.hasAlias("test", "testAlias3")).isTrue();
assertThat(registry.canonicalName("testAlias")).isEqualTo("test");
assertThat(registry.canonicalName("testAlias2")).isEqualTo("test");
assertThat(registry.canonicalName("testAlias3")).isEqualTo("test");
}
@Test // SPR-17191
void aliasChainingWithMultipleAliases() {
SimpleAliasRegistry registry = new SimpleAliasRegistry();
registry.registerAlias("name", "alias_a");
registry.registerAlias("name", "alias_b");
assertThat(registry.hasAlias("name", "alias_a")).isTrue();
assertThat(registry.hasAlias("name", "alias_b")).isTrue();
registry.registerAlias("real_name", "name");
assertThat(registry.hasAlias("real_name", "name")).isTrue();
assertThat(registry.hasAlias("real_name", "alias_a")).isTrue();
assertThat(registry.hasAlias("real_name", "alias_b")).isTrue();
registry.registerAlias("name", "alias_c");
assertThat(registry.hasAlias("real_name", "name")).isTrue();
assertThat(registry.hasAlias("real_name", "alias_a")).isTrue();
assertThat(registry.hasAlias("real_name", "alias_b")).isTrue();
assertThat(registry.hasAlias("real_name", "alias_c")).isTrue();
}
}
已经使用了 ConcurrentHashMap 为啥还要使用 synchronized ?
在注册别名时,检查别名是否注册过名称这一步,如果不对注册表加锁,会导致检查出现问题,最终导致出现重复引用

两个注册过程并发进行,在检查时两个注册过程均未发现问题,但是注册过后便会出现问题


Spring-AliasRegistry的更多相关文章
- 死磕Spring源码之AliasRegistry
死磕Spring源码之AliasRegistry 父子关系 graph TD; AliasRegistry-->BeanDefinitionRegistry; 代码实现 作为bean定义的最顶层 ...
- Spring之28:AliasRegistry&SimpleAliasRegistry
AliasRegistry接口定义了alias的基本操作. package org.springframework.core; public interface AliasRegistry { //对 ...
- Spring源码分析——BeanFactory体系之抽象类、类分析(一)
上一篇介绍了BeanFactory体系的所有接口——Spring源码分析——BeanFactory体系之接口详细分析,本篇就接着介绍BeanFactory体系的抽象类和接口. 一.BeanFactor ...
- Spring源码分析——BeanFactory体系之接口详细分析
Spring的BeanFactory的继承体系堪称经典.这是众所周知的!作为Java程序员,不能错过! 前面的博文分析了Spring的Resource资源类Resouce.今天开始分析Spring的I ...
- 深入剖析 Spring 框架的 BeanFactory
说到Spring框架,人们往往大谈特谈一些似乎高逼格的东西,比如依赖注入,控制反转,面向切面等等.但是却忘记了最基本的一点,Spring的本质是一个bean工厂(beanFactory)或者说bean ...
- Spring源码之SimpleAliasRegistry解读(一)
Spring源码之SimpleAliasRegistry解读(一) 阅读spring源码中org.springframework.core.SimpleAliasRegistry类时发现该类主要是使用 ...
- Spring MVC 解读——<context:component-scan/>
转自:http://my.oschina.net/HeliosFly/blog/203149 作者:GoodLoser. Spring MVC 解读---<context:component-s ...
- spring beans源码解读之--BeanFactory的注册
beanFactory的继承关系如下图所示: (图片来源:http://www.myexception.cn/software-architecture-design/925888.html) 在上节 ...
- Spring源码解析一(框架梳理)
整体架构 打算开始写这个系列,不为上首页,也不为博取多少关注,只有一个目的:梳理知识,扩充思路:废话不多,开始吧.第一步,大家去spring的官方github下面去下载它的源码,具体的自己谷歌,我已经 ...
- 解析spring中的BeanFactory
我们常把spring看作一个bean工厂或者ioc容器,它帮助我们负责对象的创建管理,以及对象间依赖关系的建立,还有其他的功能. 关于工厂的实现,一般来说与我们接触最多的就是BeanFactory和A ...
随机推荐
- Java实现 LeetCode 467 环绕字符串中唯一的子字符串
467. 环绕字符串中唯一的子字符串 把字符串 s 看作是"abcdefghijklmnopqrstuvwxyz"的无限环绕字符串,所以 s 看起来是这样的:"-zabc ...
- Java实现 蓝桥杯VIP 算法训练 P1102
定义一个学生结构体类型student,包括4个字段,姓名.性别.年龄和成绩.然后在主函数中定义一个结构体数组(长度不超过1000), 并输入每个元素的值,程序使用冒泡排序法将学生按照成绩从小到大的顺序 ...
- Java实现 蓝桥杯VIP 算法训练 暗恋
算法训练 暗恋 时间限制:1.0s 内存限制:256.0MB 问题描述 同在一个高中,他却不敢去找她,虽然在别人看来,那是再简单不过的事.暗恋,是他唯一能做的事.他只能在每天课间操的时候,望望她的位置 ...
- Java实现 蓝桥杯VIP 算法提高 班级排名
算法提高 班级排名 时间限制:1.0s 内存限制:256.0MB 问题描述 达达在陶陶的影响下,也对学习慢慢的产生了兴趣. 他在每次考试之后,都会追着老师问,自己在班级的总名次是多少.考试一多,老师也 ...
- 第九届蓝桥杯JavaB组省赛真题
解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.第几天 题目描述 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数 ...
- java实现求逆序对
1 问题描述 给定一个随机数数组,求取这个数组中的逆序对总个数.要求时间效率尽可能高. 那么,何为逆序对? 引用自百度百科: 设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相 ...
- java实现第五届蓝桥杯信号匹配
信号匹配 从X星球接收了一个数字信号序列. 现有一个已知的样板序列.需要在信号序列中查找它首次出现的位置.这类似于串的匹配操作. 如果信号序列较长,样板序列中重复数字较多,就应当注意比较的策略了.可以 ...
- java实现第四届蓝桥杯梅森素数
梅森素数 题目描述 如果一个数字的所有真因子之和等于自身,则称它为"完全数"或"完美数" 例如:6 = 1 + 2 + 3 28 = 1 + 2 + 4 + 7 ...
- java实现第五届蓝桥杯猜年龄
猜年龄 题目描述 小明带两个妹妹参加元宵灯会.别人问她们多大了,她们调皮地说:"我们俩的年龄之积是年龄之和的6倍".小明又补充说:"她们可不是双胞胎,年龄差肯定也不超过8 ...
- thinkphp5升级thinkphp6完整步骤
在php.ini文件中 打开 php_openssl扩展,去掉前面的;extension=php_openssl.dll 在phpstudy的WWW目录打开cmd,输入composer creat ...