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 525 连续数组
525. 连续数组 给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组(的长度). 示例 1: 输入: [0,1] 输出: 2 说明: [0, 1] 是具有相同数量0和1的最长连续 ...
- Java实现 LeetCode 386 字典序排数
386. 字典序排数 给定一个整数 n, 返回从 1 到 n 的字典顺序. 例如, 给定 n =1 3,返回 [1,10,11,12,13,2,3,4,5,6,7,8,9] . 请尽可能的优化算法的时 ...
- java实现第五届蓝桥杯奇怪的分式
奇怪的分式 题目描述 上小学的时候,小明经常自己发明新算法.一次,老师出的题目是: 1/4 乘以 8/5 小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png) 老师刚想批 ...
- 关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优化,你该了解这些
前言 相信大部分开发人员,或多或少都看过或写过并发编程的代码.并发关键字除了Synchronized(如有不懂请移至传送门,关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优 ...
- 移动端响应式布局,rem动态更新
(function(){ var fontSizeMatchDeviceWidth = function(){ var deviceWidth = document.documentElement.c ...
- CSS中的百分比(%)如何使用???
除了height以外垂直方向上的margin-top(bottom)或者padding-top(bottom)的百分比取值都是相对于父元素的宽度 在默认的content-box盒模型下元素的width ...
- <WP8开发学习笔记>动态修改启动时导航的第一个页面(如登录前启动页为LoginPage,登录后变为MainPage)
很多时候我们需要在启动程序的时候根据状态改变初始导航页面,比如程序在启动的时候判断用户是否登录, 如果未登录则跳转到LoginPage.xaml否则跳转到MainPage界面. 这时候就要分析程序的启 ...
- Astah类图中使用list<>
如何在类图中表示如下的属性,这个问题困扰了我好久.之前百度找不着任何相关的内容,今天终于在其官方论坛找着了答案. class cMeterRecord { protected: cMeterStatu ...
- 伪造随机的User-Agent
写好爬虫的原则只有一条: 就是让你的抓取行为和用户访问网站的真实行为尽量一致 1.伪造UA字符串,每次请求都使用随机生成的UA 为了减少复杂度,随机生成UA的功能通过第三方模块库fake-userag ...
- mysql面试题总结
Mysql中的myisam与innodb的区别? InnoDB存储引擎的四大特性? 什么是事务? 数据库事务的四大特性? 不考虑事务的隔离性,会发生几种问题? MySQL数据库提供的四种隔离级别? 有 ...