使用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
?
在注册别名时,检查别名是否注册过名称这一步,如果不对注册表加锁,会导致检查出现问题,最终导致出现重复引用
两个注册过程并发进行,在检查时两个注册过程均未发现问题,但是注册过后便会出现问题