Springboot 别名管理源码分析

简介: # 别名的注册SimpleAliasRegistry维护了一个映射别名到真实名称的aliasMap这里使用的是线程安全的ConcurrentHashMap,即在多线程的情况下也可以对同一个bean安全地进行别名的增删改查。同时也说明了**一个别名只能对应一个真名,而一个真名没有对应多个别名**。由于SimpleAliasRegistry是对项目全局别名的管理,**任意别名都必须是全局唯一的**。

别名的注册

SimpleAliasRegistry维护了一个映射别名到真实名称的aliasMap这里使用的是线程安全的ConcurrentHashMap,即在多线程的情况下也可以对同一个bean安全地进行别名的增删改查。同时也说明了一个别名只能对应一个真名,而一个真名没有对应多个别名。由于SimpleAliasRegistry是对项目全局别名的管理,任意别名都必须是全局唯一的
别名的注册主要用到了方法registerAlias,调用了checkForAliasCircle用于检查别名和真名是否造成了循环。这说明了bean的所有别名、真名之间都不可以出现循环注册

// 映射别名到真实名称的关系
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

// 用于注册别名
public void registerAlias(String name, String alias) {
        Assert.hasText(name, "'name' must not be empty");
        Assert.hasText(alias, "'alias' must not be empty");
        // 线程安全
        synchronized (this.aliasMap) {
            if (alias.equals(name)) {
                // 如果要注册的别名是和真实名称相同,则说明不需要这个别名,在aliasMap中可以移除这个别名对应的项
                this.aliasMap.remove(alias);
                if (logger.isDebugEnabled()) {
                    logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
                }
            }
            else {
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null) {
                    //别名已经被注册
                    if (registeredName.equals(name)) {
                        //如果和输入的真名相同,则说明已存在该别名和真名的映射,不需要重复注册
                        return;
                    }
                    //别名已被其它真名注册,发生overriding
                    if (!allowAliasOverriding()) {
                        // 如果不允许overriding,则抛出异常。默认allowAliasOverriding为true(允许)

                        throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                                name + "': It is already registered for name '" + registeredName + "'.");
                    }
                    if (logger.isDebugEnabled()) {
                        // 可以override别名,增加日志信息,说明该别名对应的真名被覆盖重写了
                        logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
                                registeredName + "' with new target name '" + name + "'");
                    }
                }
                // 检查是否造成循环,向aliasMap中插入这个对应关系
                checkForAliasCircle(name, alias);
                this.aliasMap.put(alias, name);
                if (logger.isTraceEnabled()) {
                    logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
                }
            }
        }
    }

// 确保不存在从name到alias的映射
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");
        }
    }
// 例如name = A,alias = a,aliasMap中有(a, A)或者aliasMap中有(a, x)和(x, A),则说明存在从别名a到真名A的映射
public boolean hasAlias(String name, String alias) {
        String registeredName = (String)this.aliasMap.get(alias);
        return ObjectUtils.nullSafeEquals(registeredName, name) || registeredName != null && this.hasAlias(name, registeredName);
    }

别名的获取

// 返回一个真名对应的所有别名
public String[] getAliases(String name) {
        List<String> result = new ArrayList<>();
        synchronized (this.aliasMap) {
            retrieveAliases(name, result);
        }
        return StringUtils.toStringArray(result);
    }
// 获取一个真实name对应的所有别名,在aliasMap中逐个对value进行比较,注意这是一个内部方法,外部调用统一使用的是registerAlias
private void retrieveAliases(String name, List<String> result) {
        this.aliasMap.forEach((alias, registeredName) -> {
            if (registeredName.equals(name)) {
                result.add(alias);
                retrieveAliases(alias, result);
            }
        });
    }

获取别名对应的真实名称

当aliasMap中的key中不存在name时,说明这个name没有被注册为别名,则说明它对应的真名就是自己。如果被注册为别名了,则循环不断找到最后的真名。如aliasMap中有(a, b), (b, c)就说明name = a时返回的最终真名是c,这里的b可以视为一个间接别名。

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;
    }
目录
相关文章
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue个人健康管理网站设计和实现(源码+LW+部署讲解)
基于SpringBoot+Vue个人健康管理网站设计和实现(源码+LW+部署讲解)
77 7
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的宠物饲养管理APP的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的宠物饲养管理APP的详细设计和实现(源码+lw+部署文档+讲解等)
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的二手家电管理平台的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的二手家电管理平台的详细设计和实现(源码+lw+部署文档+讲解等)
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的个人健康管理网站的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的个人健康管理网站的详细设计和实现(源码+lw+部署文档+讲解等)
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物业新冠疫情物资管理平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物业新冠疫情物资管理平台附带文章源码部署视频讲解等
20 2
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物饲养管理APP附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物饲养管理APP附带文章源码部署视频讲解等
35 1
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的在线学习过程管理系统软件附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的在线学习过程管理系统软件附带文章源码部署视频讲解等
36 1
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的学生毕业管理小程序附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的学生毕业管理小程序附带文章源码部署视频讲解等
39 1
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的高校社团管理小程序的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的高校社团管理小程序的详细设计和实现(源码+lw+部署文档+讲解等)
39 1
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的洛川县苹果销售管理平台的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的洛川县苹果销售管理平台的详细设计和实现(源码+lw+部署文档+讲解等)
45 0