Tips:此类在Spring中都是被Bean定义、创建的时候继承使用,和Bean的定义相关联
为了便于理解,我这里把最重要的一个方法:registerAlias(String name, String alias)画出逻辑流程图如下:
源码步骤分析
首先,使用了一个线程安全的Map:ConcurrentHashMap当做注册表来缓存alias和name的对应关系。key为alias别名,value为name真实值。可以通过别名获取到真实值,进而获取到Bean实例
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
为了方便大家下面阅读源码更好的理解为什么这么做?我在这里先说明两点:
1、需要避免循环别名和name之间循环引用的问题。比如a->b b->c c->a这就循环引用了,是需要避免的,否则很容易出问题
2、不能出现并发问题直接出现如下情况。a -> b b->a (其实也是一种循环引用嘛)
首先我们看一下registerAlias方法,也是最为复杂点的方法:
@Override public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); //此处注意:很多人疑问的地方,用了ConcurrentHashMap,为何此处还要加锁呢?有必要吗? //答:非常有必要的。因为ConcurrentHashMap只能保证单个put、remove方法的原子性。而不能保证多个操作同时的原子性。比如我一边添加、一边删除 显然这是不被允许的 synchronized (this.aliasMap) { //若发现别名和name是相同的,就不需要做啥了。而且顺手把这个key给移除掉 if (alias.equals(name)) { this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } } else { //拿到这个别名对应的name,看看该别名是否已经存在对应的name了 String registeredName = this.aliasMap.get(alias); if (registeredName != null) { //若已经存在对应的name了,而且还和传进俩的name相同,那啥都不做就行 if (registeredName.equals(name)) { return; } //若存在对应的name了,切还不让复写此别名(让其指向别的name),那就跑错吧 if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } checkForAliasCircle(name, alias); this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } }
当alias对应的name不存在的时候,还需要去检查是否可能存在循环引用现象:checkForAliasCirclee如下:
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方法如下:
public boolean hasAlias(String name, String alias) { for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) { String registeredName = entry.getValue(); //若找到了此name 然后就拿出其对应的alias(可能会有多次哦) if (registeredName.equals(name)) { String registeredAlias = entry.getKey(); //如果此alias和传入的alias相同,返回true 证明name有这个alias //一般人可能上面那一步就算了直接return了,但是,但是,但是还有一种情况也必须考虑到:倘若这个已经注册过的registeredAlias和传入的alias不相等。 //但是把他作为name去找是否有alias的时候,如果有也得判断是true,表示有。 防止了a -> b b->c c->a的循环的情况 此处处理可以说是非常的优雅和谨慎了~ if (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias)) { return true; } } } return false; }
该方法旨在判断:给定的name,是否已经有了对应的alias别名呢?
注册的难点在于如何防止重现名称和别名之间的重复引用。
最后用一张图形象的解释一下,为什么需要加锁?
在注册别名时,检查别名是否注册过名称这一步,如果不对注册表加锁,会导致检查出现问题,最终导致出现重复引用
两个注册过程并发进行,在检查时两个注册过程均未发现问题,但是注册过后便会出现问题




