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)