写点什么

Spring-AliasRegistry

用户头像
CoderLi
关注
发布于: 2020 年 06 月 15 日

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


@Overridepublic void removeAlias(String alias) {   synchronized (this.aliasMap) {      String name = this.aliasMap.remove(alias);      if (name == null) {         throw new IllegalStateException("No alias '" + alias + "' registered");      }   }}
@Overridepublic boolean isAlias(String name) { return this.aliasMap.containsKey(name);}
@Overridepublic 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); } });}
复制代码


上面的方法实现相对而已是比较简单的


@Overridepublic 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


发布于: 2020 年 06 月 15 日阅读数: 61
用户头像

CoderLi

关注

微信公众号:CoderLi ,专注于 Java 后端开发 2019.07.14 加入

还未添加个人简介

评论

发布
暂无评论
Spring-AliasRegistry