Mybatis 源码学习(10)-类型转换(TypeAliasRegistry)

历史文章:
Mybatis 源码学习(9)-类型转换(TypeHandlerRegistry)


TypeAliasRegistry 的逻辑比较简单,它负责为某个数据类型创建别名,之后在使用时可以使用别名引用该类。在 Mybatis 的 mapper.xml 文件中,一般会有返回值的类型,对于整型数值,既可以用 int 也可以用 integer,还可以用类的全限定名,这里就是用到了 Integer 的别名。

TypeAliasRegistry 的核心字段是 TYPE_ALIASES,它是别名到实际类的 Class 对象之间映射关系的 Map 对象,TYPE_ALIASES 的构造依赖于 registerAlias 方法。

// 别名与时间类的 Class 对象的映射关系
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

// 注册别名
public void registerAlias(String alias, Class<?> value) {
  if (alias == null) { // 如果别名为 null,直接抛出异常
    throw new TypeException("The parameter alias cannot be null");
  }
  // 别名全部使用小写
  String key = alias.toLowerCase(Locale.ENGLISH);
  // 检测别名是否已经存在,重复注册则抛出异常
  if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
    throw new TypeException(“…”);
  }
  // 注册别名
  TYPE_ALIASES.put(key, value);
}

TypeAliasRegistry 的构造方法中,默认为Java 的基本类型及其数组类型、基本类型的包装类型及其数组类型、Date、BigDecimal、BigInteger、Map、HashMap、List、ArrayList、Collection、Iterator、ResultSet 等类型添加了别名。

public TypeAliasRegistry() {
  // 以 int 为例,其他类型的注册逻辑省略
  // …

  // 注册包装类型
  registerAlias("int", Integer.class);
  registerAlias("integer", Integer.class);

  // 注册包装类型数组
  registerAlias("int[]", Integer[].class);
  registerAlias("integer[]", Integer[].class);

  // 注册基本类型
  registerAlias("_int", int.class);
  registerAlias("_integer", int.class);

  // 注册基本类型数组
  registerAlias("_int[]", int[].class);
  registerAlias("_integer[]", int[].class);
}

除了基本的注册单个类型的别名方法外,TypeAliasRegistry 还提供自动扫描包路径下具有 @Alias 注解的类。registerAliases(String packageName, Class<?> superType) 方法会扫描指定包下的类,并为指定类的子类添加别名;registerAlias(Class<?> type) 会尝试读取类上的 @Alias 注解实现注册。

// 扫描整个包下的类,以实现别名注册
public void registerAliases(String packageName, Class<?> superType) {
  // 查找整个包下继承 superType 的子类
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
  for(Class<?> type : typeSet){
    // 过滤掉匿名类、接口以及成员类
    if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
      registerAlias(type);
    }
  }
}

// 解析 @Alias 注解以实现注册
public void registerAlias(Class<?> type) {
  String alias = type.getSimpleName(); // 类名(仅包含名称)
  // 读取 @Alias 注解
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  }
  // 检测该别名不存在时,将该别名注册至 TYPE_ALIASES 集合中
  registerAlias(alias, type);
}

获取别名的方法是 resolveAlias(String string),当别名存在时,返回的是 TYPE_ALIASES 中注册的别名,当别名不存在时,则尝试通过反射创建给定的类型的 Class 对象。

public <T> Class<T> resolveAlias(String string) {
  try {
    if (string == null) { // 给定别名是 null,直接返回 null
      return null;
    }
    // 全部改为小写
    String key = string.toLowerCase(Locale.ENGLISH);
    Class<T> value;
    if (TYPE_ALIASES.containsKey(key)) {
      // 如果是已注册的别名,直接返回
      value = (Class<T>) TYPE_ALIASES.get(key);
    } else {
      // 是未注册过的别名,则反射生成对应的 Class 对象
      value = (Class<T>) Resources.classForName(string);
    }
    return value;
  } catch (ClassNotFoundException e) {
    throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  }
}

总结

TypeAliasRegistry 可以实现别名注册逻辑,其内部的核心字段 TYPE_ALIASES 是别名的 Map 集合,key 是别名的小写,value 是别名的 Class 对象。设置别名时,即可以直接指定类型,也可以通过自动扫描 @Alias 注解实现,获取别名时,如果查询到已有的别名,则直接返回,否则就以反射的方式创建对应的类型。


参考文档:《Mybatis 技术内幕》

本文的基本脉络参考自《Mybatis 技术内幕》,编写文章的原因是希望能够系统地学习 Mybatis 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。


欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值