您的位置 首页 java

mybatis-configuration容器(1)–别名注册表–TypeAliasRegistry

mybatis的别名注册表 TypeAliasRegistry ,也是 mybatis 管理的一个别名容器,同样也是存放在Configuration中的。别名的好处就是: 当使用时不用再去写类的全限定名(包名.类名),使用简单的类名(全小写)即可 。使用多见与以下场景:

  • ResultMap中的type,resultType,ofType,javaType, Type Handler等属性。

1.别名的注册

1.1.别名注册详解

TypeAliasRegistry为Configuration的成员变量,当初始化Configuration对象时,会执行如下动作:

  1. 首先会创建TypeAliasRegistry对象,将type相关的alia注册到TypeAliasRegistry中。
  2. 然后将Configuration相关的typeAlias注册到TypeAliasRegistry。
 public class TypeAliasRegistry {

    private final Map<String, Class<?>> typeAliases = new HashMap<>();

    public TypeAliasRegistry() {
        registerAlias("string", String.class);
        registerAlias(" Byte ", Byte.class);
        registerAlias("long", Long.class);
        // 省略......
    }
}

public class Configuration {
    
    // 1.创建TypeAliasRegistry对象
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    
    public Configuration() {
        // 2.将相关typeAlias注册进来
        typeAliasRegistry.registerAlias(" Jdbc ", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
        // 省略......
    }
}  

最终类的别名都会存到TypeAliasRegistry的typeAliases变量中,该变量为HashMap结构.key为别名(全小写).value为key对应类的Class对象,用于框架反射创建实例。 结构见下图

TypeAliasRegistry与Configuration关系图

别名注册方法详解( 码-1 )

 public class TypeAliasRegistry{
    
    private final Map<String, Class<?>>  type Aliases = new HashMap<>();
    
    // 只有Class,没有别名.这里会推测alias
    public void registerAlias(Class<?> type) {
        // 类名(除包名)
        String alias = type.getSimpleName();
        Alias aliasAnnotation = type.getAnnotation(Alias.class);
        // 如果类上加了@Alias注解,那么alias = @Alias的value属性
        if (aliasAnnotation != null) {
            alias = aliasAnnotation.value();
        }
        registerAlias(alias, type);
    }
    
    // 这方法会直接将别名放入typeAliases容器中
    public void registerAlias(String alias, Class<?> value) {
        if (alias == null) {
            throw new TypeException("The parameter alias cannot be null");
        }
        // issue #748
        // 全部转小写
        String key = alias.toLowerCase(Locale.ENGLISH);
        if (typeAliases.containsKey(key) && 
            typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
            throw new TypeException("The alias '" + alias + "' is already mapped to the value '" 
                                    + typeAliases.get(key).getName() + "'.");
        }
        // 存储结果,见上图
        typeAliases.put(key, value);
    }
}  

1.2.系统默认别名列表

左侧为Configuration初始化时生成,右侧为TypeAliasRegistry初始化时生成。

mybatis默认初始化typeAlias数据集

2.自定义别名注册

别名注册分为两种形式,分别为:扫描包注册;单个类注册

2.1.扫描包

 private void typeAliasesElement(XNode parent) {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
                String typeAliasPackage = child.getStringAttribute("name");
                configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
            } else {
                // 省略......
            }
        }
    }
}

public class ResolverUtil<T> {

    // 匹配到的Class
    private Set<Class<? extends T>> matches = new  Hash Set<>();

    public void registerAliases(String packageName) {
        registerAliases( package Name, Object.class);
    }

    public void registerAliases(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
        // 这里判断该包下是否都是Object的子类(上面的方法传过来的是Object.class),
        // 如果是将该包下的所有类放到成员变量matches中
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
        // 遍历matches
        for (Class<?> type : typeSet) {
            // Ignore inner classes and interfaces (including package-info.java)
            // Skip also inner classes. See issue #6
            // 当type不是匿名类、不是接口、不是成员类时,注册该type
            if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
                // 注册参照【码-1】
                registerAlias(type);
            }
        }
    }
}  

2.2.单个类

 private void typeAliasesElement(XNode parent) {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
       // 省略......
            } else {
                String alias = child.getStringAttribute("alias");
                String type = child.getStringAttribute("type");
                try {
                    // 根据我们写的类的全限定名,反射获取Class
                    Class<?> clazz = Resources.classForName(type);
                    // 注册参照【码-1】
                    if (alias == null) {
                        typeAliasRegistry.registerAlias(clazz);
                    } else {
                        typeAliasRegistry.registerAlias(alias, clazz);
                    }
                } catch (ClassNotFoundException e) {
                    throw new BuilderException("Error registering typeAlias for '" + alias
                                               + "'. Cause: " + e, e);
                }
            }
        }
    }
}  

3.别名的使用

通过别名找Class的方法为: 通过别名寻找.如果没有找到,通过将参数作为类的全限定名获取Class.这就是为什么当我们的resultType设置为int或integer时,mybatis也会自动帮我们映射成 Integer (参照1.2.系统默认别名列表).至于从怎么从Integer寻找相应的类型处理器,我们第二章接着分享.

 public <T> Class<T> resolveAlias(String string) {
    try {
        if (string == null) {
            return null;
        }
        // issue #748
        String key = string.toLowerCase(Locale.ENGLISH);
        Class<T> value;
        // 看这里,如果有别名的map中有这个key,那么就直接取出相应的Class即可
        if (typeAliases.containsKey(key)) {
            value = (Class<T>) typeAliases.get(key);
        // 如果没有,根据Class.forName(),获取Class,如果这里不是写的类的全限定名,会保错ClassNotFound
        } else {
            value = (Class<T>) Resources.classForName(string);
        }
        return value;
    } catch (ClassNotFoundException e) {
        throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
}  

4.结论

注册时:

  1. 别名注册时,首先判断类上是否有 @Alias 注解,如果有则获取 @Alias的value 属性,作为别名的初始值;如果没有则获取 类名 (不含包名)作为别名的初始值( 具体方法Class.getSimpleName() )。
  2. 将别名初始值的 小写 作为key,类的Class作为value放入TypeAliasRegistry的容器中. 注意:当有两个重名的别名时(包不同,但类名相同),会报错。

使用时:

  1. 将别名全部转换为小写,从TypeAliasRegistry容器中获取,所以 当我们使用别名时,注意是小写,不是驼峰。
  2. 如果没有获取到,会根据传入的别名进行Class.forName(),如果存在,在获取Class成功,否则会报ClassNotFoundException。

文章来源:智云一二三科技

文章标题:mybatis-configuration容器(1)–别名注册表–TypeAliasRegistry

文章地址:https://www.zhihuclub.com/197455.shtml

关于作者: 智云科技

热门文章

网站地图