功能:注册/存放别名
唯一成员变量为
1
| private final Map<String, String> aliasMap = new ConcurrentHashMap(16);
|
这Map<String,String> 其实key是alias,value是name. 刚开始没注意可能搞反了呢
#
注册别名registerAlias()
举例来说 我们要注册的beanName为myTestBean , 别名为mtb
校验name和alias都不为空
如果name和alias一样,那map里删了这条name,结束
依据alias 即 mtb去map里面取已注册的name,如果真的有已注册过的话:
registeredName
和name
相等的话,那就不用管,结束.- 如果他俩不相等,这个工厂又不允许重写alias,那就抛异常! (
allowAliasOverriding()
这个方法在SimpleAliasRegistry的子类里面有的会被重写.
如果前面都通过了,this.checkForAliasCircle(name, alias);
再次循环检查一遍,判断是否hasAlias()
,如果是true的话,就报错,不是的话,走第5步. 具体hasAlias()
流程如下:
- 循环找出
registeredName
为myTestBean的那组键值对 (这里和上面的遍历不一样哦,这里是根据直接找重复的beanName的,上面是找重复的alias的 - 如果这个键值对的key即alias也是等于mtb的话,报错 (但是实际我们这第三步也查过这种情况)
- 如果这个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
如果前面各种校验全部通过 ,this.aliasMap.put(alias, name);
很简单,map里面塞值吧!
前面校验看着很啰嗦,其实总结起来都很简单
- 键值都不能为空
- 别键值相等,我会删了这个别名的(虽然你应该本来就添加不进来)
- 别名已用过,如果还是原name,那不用操作
- 别名已用过,这次换了新name,那就看你注册器允不允许我搞覆盖咯
- 别告诉我这个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;
}
|
方法很简单,就是一层一层地去找这个名字的本名.
#
AbstractBeanFactory
对getAliases()
的重写
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多余了,没作用.
否则
如果解析后别名和解析前别名一致,那么把解析前别名 alias指向解析后的本名,resolvedName (这里就直接覆盖掉原alias的映射了
如果解析后别名和解析前不一致
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,每个键值对都解析一遍,
如果这对能解析出新的一对合法别名–本名键值对,那么就把之前的那对删掉,注册新的这个解析后的一对,
此外检验下解析后的别名是不是被占用,如果占用了还指向错了值,就抛异常,
如果占用了也是指向解析后名字,那就说明解析后的这对已经注册好了,只需删掉旧的那对