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