SimpleAliasRegistry

功能:注册/存放别名

唯一成员变量为

1
private final Map<String, String> aliasMap = new ConcurrentHashMap(16);

这Map<String,String> 其实key是alias,value是name. 刚开始没注意可能搞反了呢

# 注册别名registerAlias()


举例来说 我们要注册的beanName为myTestBean , 别名为mtb

  1. 校验name和alias都不为空

  2. 如果name和alias一样,那map里删了这条name,结束

  3. 依据alias 即 mtb去map里面取已注册的name,如果真的有已注册过的话:

    1. registeredNamename相等的话,那就不用管,结束.
    2. 如果他俩不相等,这个工厂又不允许重写alias,那就抛异常! (allowAliasOverriding() 这个方法在SimpleAliasRegistry的子类里面有的会被重写.
  4. 如果前面都通过了,this.checkForAliasCircle(name, alias); 再次循环检查一遍,判断是否hasAlias(),如果是true的话,就报错,不是的话,走第5步. 具体hasAlias()流程如下:

    1. 循环找出registeredNamemyTestBean的那组键值对 (这里和上面的遍历不一样哦,这里是根据直接找重复的beanName的,上面是找重复的alias的
    2. 如果这个键值对的key即alias也是等于mtb的话,报错 (但是实际我们这第三步也查过这种情况)
    3. 如果这个myTestBean找出来的alias是"myTB",那就还要走一遍hasAlias("myTB","mtb") ,换句话说,就是要看一看是不是有哪个bean名字叫myTB,别名叫mtb的,如果真的有,就返回true

    第四步的hasAlias() 这种判断是啥意思呢?

    想注册 mtb–myTestBean (别名–本名)

    如果已存在 mtb–myTestBean ,那么返回true

    如果已存在 myTB–myTestBean,

    ​ 且存在 mtb–myTB ,

    那就构成了 mtb–myTB–myTestBean 也返回true

    如果已存在 myTB–myTestBean,

    ​ 且存在 mta–myTB

    ​ 且存在 mtb–mta ,

    那就构成了 mtb–mta–myTB–myTestBean 还是返回true

    .

    这下看明白了吧,

    说明hasAlias()方法不止判断mtb是否已经做了myTestBean的别名,

    还判断了mtb是不是myTestBean的别名 的别名 的别名 的别名 的别名….最终能通过一条线导向myTestBean

  5. 如果前面各种校验全部通过 ,this.aliasMap.put(alias, name); 很简单,map里面塞值吧!

前面校验看着很啰嗦,其实总结起来都很简单

  1. 键值都不能为空
  2. 别键值相等,我会删了这个别名的(虽然你应该本来就添加不进来)
  3. 别名已用过,如果还是原name,那不用操作
  4. 别名已用过,这次换了新name,那就看你注册器允不允许我搞覆盖咯
  5. 别告诉我这个alias居然还是那个name的别名的别名…

第5种的情况让我突然想到,这种连环关系应该连申请都申请不了吧,即使真的想搞出连环关系,也应该是下面这种情况吧??

myTB–myTestBean已注册好了,新来个bean想注册beanName叫myTB

应该在申请注册beanName叫myTB的时候就校验过了吧!!!

好,翻回去找找注册beanName的时候,是不是已经校验过不能和已注册的alias重名!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    String id = ele.getAttribute("id");
    String nameAttr = ele.getAttribute("name");
  //都忘掉了bean原本没有alias属性了......
  //aliases数组是原本的name里面的多个值
  //除非之前没id,有name, 那么从aliases里remove(0)出去做beanName,剩下的继续做alias
    List<String> aliases = new ArrayList();
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
        aliases.addAll(Arrays.asList(nameArr));
    }

    String beanName = id;
    if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
        beanName = (String)aliases.remove(0);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
        }
    }
         if (containingBean == null) {
           //在这里校验一波beanName和alias的唯一性
            this.checkNameUniqueness(beanName, aliases, ele);
        }
    ....
    }

//校验重名的方法
   protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
        String foundName = null;
     //看已用名里是否包括了beanName
        if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
            foundName = beanName;
        }
    //看已用名里是否用过了这些alias
        if (foundName == null) {
            foundName = (String)CollectionUtils.findFirstMatch(this.usedNames, aliases);
        }

        if (foundName != null) {
            this.error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
        }

     //注意这个地方, beanName和aliases都是全部加入usedNames的
     //那就说明,在前面校验的时候,已用名就是包括已用beanName和已用alias的
     //所以如果myTB--myTestBean已注册好了,myTB和myTestBean都被usedName收录了
     //再想拿myTB做beanName肯定不行了
        this.usedNames.add(beanName);
        this.usedNames.addAll(aliases);
    }

所以其实是校验过的啊,感觉上面考虑的第5种情况有点多此一举呢…

还是来看看类里面的其它方法吧

# 取消注册别名removeAlias()


很简单,map直接remove这个别名,如果remove结果是null,那抛个异常

# 判断是否为别名 isAlias()


1
return this.aliasMap.containsKey(name);

# 获取某个name的所有别名 getAliases(String name)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
//加了个同步,
synchronized(this.aliasMap) {
    this.retrieveAliases(name, result);
}
 private void retrieveAliases(String name, List<String> result) {
        Iterator var3 = this.aliasMap.entrySet().iterator();
		//遍历map,找出value等于这个name的key
        while(var3.hasNext()) {
            Entry<String, String> entry = (Entry)var3.next();
            String registeredName = (String)entry.getValue();
            if (registeredName.equals(name)) {
                String alias = (String)entry.getKey();
                result.add(alias);
              //注意这里,厉害了,居然还真的递归地去找它的别名的别名(的别名的别名...)
              //到底什么地方能制造出链式的别名啊啊啊啊????
                this.retrieveAliases(alias, result);
            }
        }
    }

另外注意到,getAliases()方法在AbstractBeanFactory其实是有重写的啊,稍后去看看!

# 找本名canonicalName()


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public String canonicalName(String name) {
    String canonicalName = name;
    String resolvedName;
    do {
        resolvedName = (String)this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    } while(resolvedName != null);
    return canonicalName;
}

方法很简单,就是一层一层地去找这个名字的本名.

# AbstractBeanFactorygetAliases() 的重写


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public String[] getAliases(String name) {
  //找到这个name的真正的beanName, 
  //transformedBeanName里面的操作主要是去除头部所有&符号,以及调用上面的canonicalName()来找本名
    String beanName = this.transformedBeanName(name);
    List<String> aliases = new ArrayList();

    boolean factoryPrefix = name.startsWith("&");
    String fullBeanName = beanName;
    if (factoryPrefix) {
  	  //如果原name以&开头(可能多个&),表示工厂前缀
      //bean全名就加上一个&
        fullBeanName = "&" + beanName;
    }

    if (!fullBeanName.equals(name)) {
      //如果bean全名还不等于原名, 说明原名可能是 &&&myTestBean
      //那么把&myTestBean也作为它的一个别名
        aliases.add(fullBeanName);
    }
//调用上面我们SimpleAliasRegistry的方法
    String[] retrievedAliases = super.getAliases(beanName);
    String[] var7 = retrievedAliases;
    int var8 = retrievedAliases.length;

    for(int var9 = 0; var9 < var8; ++var9) {
        String retrievedAlias = var7[var9];
      //如果是工厂bean,配上工厂前缀&
        String alias = (factoryPrefix ? "&" : "") + retrievedAlias;
        if (!alias.equals(name)) {
          //只要不等于我们的name,通通可以做为别名 
          aliases.add(alias);
        }
    }
//如果这个beanName对应的bean不是单例,并且这个beanName还没注册bean
    if (!this.containsSingleton(beanName) && !this.containsBeanDefinition(beanName)) {
        BeanFactory parentBeanFactory = this.getParentBeanFactory();
        if (parentBeanFactory != null) {
          //并且它还有父工厂,那就从它的父工厂里面找&myTestBean的别名 ,都算进来是它的别名
            aliases.addAll(Arrays.asList(parentBeanFactory.getAliases(fullBeanName)));
        }
    }

    return StringUtils.toStringArray(aliases);
}

虽然这个方法看得还不是很明白,但大概知道,这里的重写主要是牵扯到了这个bean的工厂相关属性,待后面了解了工厂应该就能清楚这里的别名与工厂的关系了.

# 解析别名resolveAliases(StringValueResolver valueResolver)


这里的入参需要传个值解析器,

加锁,复制出map,遍历map,拿到alias,registeredName, 以及,用解析器解析后的resolvedAlias,resolvedName

如果解析后的别名,本名有一者为空,或者二者相等,那么就要从map里移除这个alias,说明这个alias多余了,没作用.

否则

  1. 如果解析后别名和解析前别名一致,那么把解析前别名 alias指向解析后的本名,resolvedName (这里就直接覆盖掉原alias的映射了

  2. 如果解析后别名和解析前不一致

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    //先找找解析后别名是不是在map里有映射
    String existingName = (String)this.aliasMap.get(resolvedAlias);
                            if (existingName != null) {
                              //如果有的话,并且还指向了其它值,那就要抛异常了
                                if (!existingName.equals(resolvedName)) {
                                    throw new IllegalStateException("Cannot register resolved alias '" + resolvedAlias + "' (original: '" + alias + "') for name '" + resolvedName + "': It is already registered for name '" + registeredName + "'.");
                                }
                            //否则直接从map里移除原来的alias别名就够了
                                this.aliasMap.remove(alias);
                                break;
                            }
                        //注册解析后的别名--解析后的本名
                            this.checkForAliasCircle(resolvedName, resolvedAlias);
                        //注意到这里和普通注册唯一的区别就是这里还要移除原alias
                            this.aliasMap.remove(alias);
                            this.aliasMap.put(resolvedAlias, resolvedName);
    

所以总结一下这个处理解析Aliases的目的

遍历map,每个键值对都解析一遍,

如果这对能解析出新的一对合法别名–本名键值对,那么就把之前的那对删掉,注册新的这个解析后的一对,

此外检验下解析后的别名是不是被占用,如果占用了还指向错了值,就抛异常,

如果占用了也是指向解析后名字,那就说明解析后的这对已经注册好了,只需删掉旧的那对