SpringAliasRegistry

7次阅读

共计 4347 个字符,预计需要花费 11 分钟才能阅读完成。

使用 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/f641385…

正文完
 0