功能:注册/存放别名

唯一成员变量为

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

这Map 其实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,每个键值对都解析一遍,

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

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

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