使用Spring 的時候咱們能夠很容易的爲某個bean 配置一個或多個別名web
<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"/>
spring
使用 @Bean 註解的時候併發
@Bean(value = {"aaa", "bbb", "ccc"})
複製代碼
那麼 除了第一個是這個 beanName(bean id) 以外、其餘的都是 aliasapp
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)] )ide
@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 擁有 別名testthis
這個關係、那麼就檢查、看看再已用的關係中是否已經有 test擁有別名testAlias01 關係、若是有則是 別名的循環依賴spa
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