Mybatis 源码学习-类型转换(TypeHandlerRegistry)
Posted 凉茶方便面
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 源码学习-类型转换(TypeHandlerRegistry)相关的知识,希望对你有一定的参考价值。
历史文章:
Mybatis 源码学习(8)-类型转换(TypeHandler)
定义 TypeHandler 后,Mybatis 还需要对这些 TypeHandler 进行管理,Mybatis 是通过 TypeHandlerRegistry 来实现 TypeHandler 的管理的。TypeHandlerRegistry 的初始化是在 Configuration 中,Configuration 包含一个 private final 字段,直接初始化了 TypeHandlerRegistry,而 TypeHandlerRegistry 的初始化阶段,会构建 JDBC 数据类型、Java 数据类型、TypeHandler 之间的关系,便于索引查询。
TypeHandlerRegistry 中的核心字段主要是几个关系 Map:
// JdbcType 与TypeHandler 之间的映射关系,JdbcType 是枚举类型,它与 JDBC 规范中的数据类型一一对应
// 该集合的作用是从 ResultSet 中读取数据时,将 JdbcType 转化为 Java 类型
// (由于 JdbcType 数量已知,因此该集合中的 TypeHandler 数据量固定)
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP =
new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
// Java 类型与 TypeHandler 之间的映射关系,但是由于 Java 类型可以转化为多种 JdbcType,
// 因此 value 也是一个 Map(因为 String 可以转化为 char、varchar、text 等数据库类型)
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP =
new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
// 全部 TypeHandler 的类型与 TypeHandler 实例之间的映射关系(可以认为是缓存)
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP =
new HashMap<Class<?>, TypeHandler<?>>();
// 空TypeHandler 集合的标志
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP =
Collections.emptyMap();
注册 TypeHandler 的方法:register
TypeHandler.register() 方法实现了 TypeHandler 的注册逻辑,其注册过程主要是解析 TypeHandler 并向以上 Map 中存储对应的解析结果。register 方法是一系列重载的方法,其中大部分方法是做数据类型转化,并调用其他重载方法,比较核心的方法有 6 个。
以上方法中,方法 4 是最核心的方法,该方法的参数包括:该 TypeHandler 能处理的 JavaType,JdbcType以及TypeHandler 对象,该方法的核心逻辑是向 TYPE_HANDLER_MAP 和 ALL_TYPE_HANDLERS_MAP 注册对应的 TypeHandler。
// 方法 4 的实现
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)
// 是否明确指定能够处理的 javaType
if (javaType != null)
// 获取 javaType 在 TYPE_HANDLER_MAP 中已注册的 TypeHandler
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
// 如果之前尚未注册过 TypeHandler,则新建对应的 Map
if (map == null || map == NULL_TYPE_HANDLER_MAP)
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
// 将 TypeHandler 注册至 TYPE_HANDLER_MAP
map.put(jdbcType, handler);
// 向 ALL_TYPE_HANDLERS_MAP 注册 TypeHandler 的类型和 TypeHandler 对象
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
方法 1~3 最终都会调用方法 4 进行注册,它们做了一些前置的工作,主要是解析方法上的 @MappedTypes
和 @MappedJdbcTypes
注解,@MappedTypes
表示 TypeHandler 可以用于的 Java 类,@MappedJdbcTypes
表示 TypeHandler 可以处理的 JDBC 数据类型。
// 方法 1,方法包含 typeHandlerClass 参数
public void register(Class<?> typeHandlerClass)
boolean mappedTypeFound = false;
// 解析获取 @MappedTypes 注解
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null)
// 根据 @MappedTypes 上指定的 Java 类型,逐个注册
for (Class<?> javaTypeClass : mappedTypes.value())
// 经过包含类型转化和实例化 TypeHandler 的 register 方法转发,最终交给方法 3 进行处理
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
if (!mappedTypeFound)
// 未指定 @MappedTypes 注解,直接交给方法 2 处理
register(getInstance(null, typeHandlerClass));
// 方法 2
public <T> void register(TypeHandler<T> typeHandler)
boolean mappedTypeFound = false;
// 检查 @MappedTypes 注解,根据指定的 Java 类型进行注册,逻辑同方法 1
MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
if (mappedTypes != null)
for (Class<?> handledType : mappedTypes.value())
register(handledType, typeHandler);
mappedTypeFound = true;
// 从 3.1.0 开始,可以根据 TypeHandler 的类型自动查找对应 Java 类型,但是需要 TypeHandler 继承 TypeReference
// 它会自动解析 TypeHandler 上指定的泛型参数,并以该泛型参数作为 Java 类型
if (!mappedTypeFound && typeHandler instanceof TypeReference)
try
TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
// 由重载方法 3 继续处理
register(typeReference.getRawType(), typeHandler);
mappedTypeFound = true;
catch (Throwable t)
if (!mappedTypeFound)
register((Class<T>) null, typeHandler);
// 方法 3,解析 @MappedJdbcTypes 注解
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler)
// 获取 @MappedJdbcTypes 注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null)
// 根据 @MappedJdbcTypes 注解指定的JDBC 类型进行注册
for (JdbcType handledJdbcType : mappedJdbcTypes.value())
register(javaType, handledJdbcType, typeHandler); // 重载方法 4
// 该 TypeHandler 能否处理 null 类型
if (mappedJdbcTypes.includeNullJdbcType())
register(javaType, null, typeHandler); // 重载方法 4
else
register(javaType, null, typeHandler); // 重载方法 4
以上方法最终均调用了方法 4,向 TYPE_HANDLER_MAP 和 ALL_TYPE_HANDLERS_MAP 完成注册,重载方法 5 是唯一一个向 JDBC_TYPE_HANDLER_MAP 注册的方法。
// 方法 5
public void register(JdbcType jdbcType, TypeHandler<?> handler)
// 注册 jdbcType 对应的 TypeHandler
JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
除了能够注册单个 TypeHandler 外,TypeHandlerRegistry 还提供了扫描整个 package 下的 TypeHandler 接口实现类,并完成 TypeHandler 的自动注册工作。
// 方法 6,扫描整个 package 下的 TypeHandler 实现类
public void register(String packageName)
// 扫描整个 package 下实现 TypeHandler 的类
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
for (Class<?> type : handlerSet)
// 过滤掉内部类、接口、抽象类,这些类无法被直接实例化
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers()))
register(type); // 由方法 1 完成注册
除了以上 register 方法外,TypeHandlerRegistry 的构造方法完成了基本的类型注册逻辑,由于数据类型过多,仅以 String 类型来说明问题。
public TypeHandlerRegistry()
// 省略其他类型的注册逻辑
// 向 TYPE_HANDLER_MAP 和 ALL_TYPE_HANDLERS_MAP 完成注册,StringTypeHandler 能够正确处理String 的 null 到 JDBC 的 null 的转化
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
// StringTypeHandler 能够完成 JDBC 的 varchar 到 Java 的 String 之间的相互转化
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
查找 TypeHandler 的方法
和注册方法相同,查找 TypeHandler 也存在多个重载的情况,TypeHandlerRegistry.getTypeHandler() 的核心逻辑是从以上四个集合中获取对应的元素。
getTypeHandler 的系列方法中,最核心的方法是方法 1:getTypeHandler(Type type, JdbcType jdbcType),其他方法都是经过类型转化最终调用该方法。
// 方法 1
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType)
// 不解析 ParamMap 类型
if (ParamMap.class.equals(type))
return null;
// 查找Java 类型对应的 TypeHandler 集合
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null)
// 根据当前 jdbcType 类型获取对应的 TypeHandler
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null)
// 如果未找到,则查找能够处理 null 值的 TypeHandler
handler = jdbcHandlerMap.get(null);
if (handler == null)
// 如果 jdbcHandlerMap 中只注册了一个 TypeHandler,则使用该 TypeHandler
handler = pickSoleHandler(jdbcHandlerMap);
return (TypeHandler<T>) handler;
getJdbcHandlerMap 方法会检查 TYPE_HANDLER_MAP 中是否存在对应的 TypeHandler 集合,如果当前类不存在对应的 TypeHandler,则继续向父类中搜索对应的 TypeHandler,如果最终仍未找到,则以 NULL_TYPE_HANDLER_MAP 初始化该 Java 类型对应的 TypeHandler。
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type)
// 查找 Java 类型对应的 TypeHandler 集合
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) // 检测是否为空集合标志
return null;
// 初始化 Java 类型对应的 TypeHandler 集合
if (jdbcHandlerMap == null && type instanceof Class)
Class<?> clazz = (Class<?>) type;
if (clazz.isEnum()) // 如果是枚举类型
// 在枚举类接口上向父类搜索
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz);
if (jdbcHandlerMap == null)
// 如果没有指定过任何 TypeHandler,则使用默认的 TypeHandler
register(clazz, getInstance(clazz, defaultEnumTypeHandler));
return TYPE_HANDLER_MAP.get(clazz);
else
// 在父类上搜索是否已存在 TypeHandler 集合
jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
TYPE_HANDLER_MAP.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
return jdbcHandlerMap;
// 查找 enum 类型实现接口的情况
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz)
// 遍历所有实现的接口
for (Class<?> iface : clazz.getInterfaces())
// 当前接口是否已经有 TypeHandler,如果没有,则递归向父接口查询
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(iface);
if (jdbcHandlerMap == null)
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
if (jdbcHandlerMap != null)
// 找到一个父接口上注册过的 TypeHandler
HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<JdbcType, TypeHandler<?>>();
for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet())
// 尝试使用当前的 enum 作为参数,尝试覆盖接口对应的 TypeHandler
newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
return newMap;
return null;
// 查找普通类父类的 TypeHandler
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz)
Class<?> superclass = clazz.getSuperclass();
if (superclass == null || Object.class.equals(superclass))
return null; // Object 或 null,则查找结束
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass);
if (jdbcHandlerMap != null)
return jdbcHandlerMap;
else
// 继续查找父类对应的 TypeHandler
return getJdbcHandlerMapForSuperclass(superclass);
TypeHandlerRegistry.getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType)
方法可以根据 handlerType 的类型,直接从 ALL_TYPE_HANDLERS_MAP 中获取对应的 TypeHandler。
TypeHandlerRegistry.getTypeHandler(JdbcType jdbcType) 方法可以根据指定的jdbcType 从 JDBC_TYPE_HANDLER_MAP 中获取对应的 TypeHandler。
Mybatis 除了支持默认的 TypeHandler 外,还支持从配置文件中配置 <typeHandlers>
节点,自定义添加 TypeHandler 实现类。Mybatis 初识化时,会解析对应的配置节点,并将对应的 TypeHandler 注册至 TypeHandlerRegistry 中。
总结
TypeHandlerRegistry 可以管理所有的 TypeHandler,支持注册和获取对应的 Java 类和 JDBC 类型对应的 TypeHandler。TypeHandlerRegistry 作为 Configuration 的一个字段,在 Mybatis 的执行过程中的任何阶段都可以被方便的获取和使用。另外,TypeHandler 支持扩展,允许自定义 TypeHandler,Mybatis 可以根据泛型参数,自动解析其能够处理的 Java 类。
参考文档:《Mybatis 技术内幕》
本文的基本脉络参考自《Mybatis 技术内幕》,编写文章的原因是希望能够系统地学习 Mybatis 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。
欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。
以上是关于Mybatis 源码学习-类型转换(TypeHandlerRegistry)的主要内容,如果未能解决你的问题,请参考以下文章
Mybatis 源码学习(10)-类型转换(TypeAliasRegistry)
Mybatis 源码学习(10)-类型转换(TypeAliasRegistry)
Mybatis 源码学习-类型转换(TypeHandler)
Mybatis 源码学习-类型转换(TypeHandler)