Spring-AliasRegistry

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

img
此次必定?
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息