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

Posted 凉茶方便面

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。


欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。

以上是关于Mybatis 源码学习(10)-类型转换(TypeAliasRegistry)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

Mybatis 源码学习(11)-日志模块

Mybatis 源码学习(11)-日志模块