近期在看Spring的源码,看到了一个类SimpleAliasRegistry,觉得这个类虽然小,但是代码写的很不错,下面简单解析下这个类。SimpleAliasRegistry有一个属性aliasMap,是一个ConcurrentHashMap,用来存放bean的别名。在注册别名时使用的是方法:registerAlias,这是一个接口方法,继承自AliasRegistry。下面是具体方法:
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
这个方法首先断言两个参数必须含有有效字符串。如果别名和名称相同,则移除这个别名。
在名称和别名不同时,获取这个别名对应的bean名称(这个名称有可能也是个别名)。如果该别名对应的名称有两种可能:存在或不存在。先来看看存在的情况:如果该别名对应的已注册的name和传入的name相同,则不需重复注册。然后检查别名是否允许重写,不允许则抛出异常(默认允许)。然后对别名进行循环检查:
protected void checkForAliasCircle(String name, String alias) {
if (hasAlias(alias, name)) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
这个方法只是简单调用下hasAlias方法,并将要注册的alias和name传入,这里是将aliase作为第一个参数、name作为第二个参数。下面看hasAlias方法的代码:
public boolean hasAlias(String name, String alias) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String registeredAlias = entry.getKey();
return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
}
}
return false;
}
注意这里的name是要注册测alias,而aliase是最开始注册时传入的name。这步检查的是别名是否是循环引用了。即找到以当前注册的别名为value的entity,看下这个entity的key是不是和当前注册的这个别名对应的name相等,如果是,则属于循环引用;如果不是,则递归找到以上面那个entity的key为value的entity(这里称为B),如果B的对应的key和当前要注册的name相等,则也属于是循环引用。
下面再拉看下获取给定的bean对应的所有别名。
首先获取给定bean的canonicalName,从字面就能理解是什么意思,通过以下方法获取:
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
这个方法就是根据给定的名字获取对应bean的canonicalName;在内部的Map中,所有的key都是aliase,如果一个entity的value在这个Map中没有对应的entity的key与其相等,则这个entity中的value就是一个bean的canonicalName。获取了bean的canonicalName之后就能以这个name为参数获取这个bean的所有aliase了,看下面的方法:
@Override
public String[] getAliases(String name) {
List result = new ArrayList();
synchronized (this.aliasMap) {
retrieveAliases(name, result);
}
return StringUtils.toStringArray(result);
}
锁住内部Map之后调用retrieveAliases(name, result)方法。为什么要锁住这个内部Map,可以看下CurrentHashMap的解释,简单说就是CurrentHashMap迭代方法不是线程安全的。下面看下获取所有aliase的方法:
private void retrieveAliases(String name, List<String> result) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String alias = entry.getKey();
result.add(alias);
retrieveAliases(alias, result);
}
}
}
这个方法就很简单了,获取所有以bean的canonicalName为value的entity将其key放入一个List中,然后递归的获取以当前的entity的key为value的entity将其key放入上面那个list中。
了解了这个内部Map的作用后下面两个方法就很清楚了:
@Override
public void removeAlias(String alias) {
String name = this.aliasMap.remove(alias);
if (name == null) {
throw new IllegalStateException("No alias '" + alias + "' registered");
}
}
这个方法就是移除一个别名,如果这个别名不存在则抛出IllegalStateException异常。
@Override
public boolean isAlias(String name) {
return this.aliasMap.containsKey(name);
}
因为在内部的Map中Bean的canonicalName总是作为entity的value存在的,所以只用判断当前的Map中有没有给定的key即可知道是不是别名了。
最后来看下resolveAliases这个方法,先上代码:
public void resolveAliases(StringValueResolver valueResolver) {
Assert.notNull(valueResolver, "StringValueResolver must not be null");
synchronized (this.aliasMap) {
Map aliasCopy = new HashMap(this.aliasMap);
for (String alias : aliasCopy.keySet()) {
String registeredName = aliasCopy.get(alias);
String resolvedAlias = valueResolver.resolveStringValue(alias);
String resolvedName = valueResolver.resolveStringValue(registeredName);
if (resolvedAlias == null || resolvedName == null || resolvedAlias.equals(resolvedName)) {
this.aliasMap.remove(alias);
}
else if (!resolvedAlias.equals(alias)) {
String existingName = this.aliasMap.get(resolvedAlias);
if (existingName != null) {
if (existingName.equals(resolvedName)) {
// Pointing to existing alias - just remove placeholder
this.aliasMap.remove(alias);
break;
}
throw new IllegalStateException(
"Cannot register resolved alias '" + resolvedAlias + "' (original: '" + alias +
"') for name '" + resolvedName + "': It is already registered for name '" +
registeredName + "'.");
}
checkForAliasCircle(resolvedName, resolvedAlias);
this.aliasMap.remove(alias);
this.aliasMap.put(resolvedAlias, resolvedName);
}
else if (!registeredName.equals(resolvedName)) {
this.aliasMap.put(alias, resolvedName);
}
}
}
}
首先还是先锁住内部的Map,因为CurrentHashMap只有put和remove是线程安全的。先看比较简单的分支情况:如果解析后的别名或者canonicalName为空或者是别名与canonicalName相同,则移除,与registerAlias方法的逻辑相同;如果解析后的canonicalName和现在Map中的canonicalName不同,则替换。下面是不属于上面两种情况的一个分支:
如果解析后的别名和原始别名不同,查看内部Map中以解析后的别名为key的项,如果不存在,则检查完循环引用后删除原始别名,添加解析后的别名;如果项存在,且存在项的value和解析后的name相同,只用将以原始aliase为key的entity移除即可,如果存在项的value和解析后的name不同,则说明解析后的aliase已被占用了,则抛出IllegalStateException。