使用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

在注册别名时,检查别名是否注册过名称这一步,如果不对注册表加锁,会导致检查出现问题,最终导致出现重复引用

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

https://blog.csdn.net/f641385712/article/details/85081323

Spring-AliasRegistry的更多相关文章

  1. 死磕Spring源码之AliasRegistry

    死磕Spring源码之AliasRegistry 父子关系 graph TD; AliasRegistry-->BeanDefinitionRegistry; 代码实现 作为bean定义的最顶层 ...

  2. Spring之28:AliasRegistry&SimpleAliasRegistry

    AliasRegistry接口定义了alias的基本操作. package org.springframework.core; public interface AliasRegistry { //对 ...

  3. Spring源码分析——BeanFactory体系之抽象类、类分析(一)

    上一篇介绍了BeanFactory体系的所有接口——Spring源码分析——BeanFactory体系之接口详细分析,本篇就接着介绍BeanFactory体系的抽象类和接口. 一.BeanFactor ...

  4. Spring源码分析——BeanFactory体系之接口详细分析

    Spring的BeanFactory的继承体系堪称经典.这是众所周知的!作为Java程序员,不能错过! 前面的博文分析了Spring的Resource资源类Resouce.今天开始分析Spring的I ...

  5. 深入剖析 Spring 框架的 BeanFactory

    说到Spring框架,人们往往大谈特谈一些似乎高逼格的东西,比如依赖注入,控制反转,面向切面等等.但是却忘记了最基本的一点,Spring的本质是一个bean工厂(beanFactory)或者说bean ...

  6. Spring源码之SimpleAliasRegistry解读(一)

    Spring源码之SimpleAliasRegistry解读(一) 阅读spring源码中org.springframework.core.SimpleAliasRegistry类时发现该类主要是使用 ...

  7. Spring MVC 解读——<context:component-scan/>

    转自:http://my.oschina.net/HeliosFly/blog/203149 作者:GoodLoser. Spring MVC 解读---<context:component-s ...

  8. spring beans源码解读之--BeanFactory的注册

    beanFactory的继承关系如下图所示: (图片来源:http://www.myexception.cn/software-architecture-design/925888.html) 在上节 ...

  9. Spring源码解析一(框架梳理)

    整体架构 打算开始写这个系列,不为上首页,也不为博取多少关注,只有一个目的:梳理知识,扩充思路:废话不多,开始吧.第一步,大家去spring的官方github下面去下载它的源码,具体的自己谷歌,我已经 ...

  10. 解析spring中的BeanFactory

    我们常把spring看作一个bean工厂或者ioc容器,它帮助我们负责对象的创建管理,以及对象间依赖关系的建立,还有其他的功能. 关于工厂的实现,一般来说与我们接触最多的就是BeanFactory和A ...

随机推荐

  1. Linux kernel学习(序)

    伟大的Linux kernel有几大重要模块: 1.文件系统(File System) 2.进程调度(Process Scheduler) 3.内存管理(Memory Management) 4.进程 ...

  2. Java实现 LeetCode 699 掉落的方块(线段树?)

    699. 掉落的方块 在无限长的数轴(即 x 轴)上,我们根据给定的顺序放置对应的正方形方块. 第 i 个掉落的方块(positions[i] = (left, side_length))是正方形,其 ...

  3. Java实现 LeetCode 658 找到 K 个最接近的元素(暴力)

    658. 找到 K 个最接近的元素 给定一个排序好的数组,两个整数 k 和 x,从数组中找到最靠近 x(两数之差最小)的 k 个数.返回的结果必须要是按升序排好的.如果有两个数与 x 的差值一样,优先 ...

  4. Java实现 蓝桥杯VIP 算法训练 学做菜

    算法训练 学做菜 时间限制:1.0s 内存限制:256.0MB 问题描述 涛涛立志要做新好青年,他最近在学做菜.由于技术还很生疏,他只会用鸡蛋,西红柿,鸡丁,辣酱这四种原料来做菜,我们给这四种原料标上 ...

  5. Java实现 LeetCode 229 求众数 II(二)

    229. 求众数 II 给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素. 说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1). 示例 1: 输入: [3,2, ...

  6. SQK Server实现 LeetCode 175 组合两个表

    175. 组合两个表 SQL架构 表1: Person +-------------+---------+ | 列名 | 类型 | +-------------+---------+ | Person ...

  7. 关于VMware虚拟机启动EFI/UEFI支持

    作为较新计算机和操作系统用于引导计算机的技术,可扩展固件接口 (EFI) 正在取代 BIOS.EFI 有时称为统一可扩展固件接口 (UEFI). 使用VMware创建虚拟机,默认还是会使用传统的BIO ...

  8. 详解 Seata Golang 客户端 AT 模式及其使用

    源码seata-golang 概述   我们知道 Seata Java Client 的 AT 模式,通过代理数据源,实现了对业务代码无侵入的分布式事务协调机制,将与 Transaction Coor ...

  9. 1.Go 开始搞起

    link 1. IDE Go Land 服务器激活 2. 资源 中文网站 翻译组 翻译组wiki 待认领文章 入门指南 中文文档 fork 更新 github 中如何定期使用项目仓库内容更新自己 fo ...

  10. Nginx思维导图

    1. Nginx简介 1.1 nginx概述 1.2 nginx的安装及程序环境 1.3 零复制&五种IO模型 1.4 nginx的基本配置 2. Nginx配置详解 2.1 main配置段详 ...