一、简介
TypeHandlerRegistry
类型处理器注册表注册了类型转换时需要用到的各种处理器以及与Java类型和Jdbc类型的映射关系。
二、数据结构
// 记录JdbcType和TypeHandler之间的对应关系,其中JdbcType是一个枚举类型,它定义对应的JDBC类型
// 该集合主要用于从结果集读取数据时,将数据从Jdbc类型转换成Java类型
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
// 记录了Java类型向指定的JdbcType转换时,需要使用的TypeHandler对象。例如:Java类型中的String可能
// 转换成数据库的char、varchar等多种类型,所以存在一对多的关系,所以值要用Map来存储
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
// 未知类型对象的TypeHandler
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
// 记录了全部TypeHandler的类型以及该类型相应的TypeHandler对象
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
-
JDBC_TYPE_HANDLER_MAP
:记录JdbcType和TypeHandler之间的对应关系,其中JdbcType是一个枚举类型,它定义对应的JDBC类型,该集合主要用于从结果集读取数据时,将数据从Jdbc类型转换成Java类型。 -
TYPE_HANDLER_MAP
:记录了Java类型向指定的JdbcType转换时,需要使用的TypeHandler对象。例如:Java类型中的String可能转换成数据库的char、varchar等多种类型,所以存在一对多的关系,所以值要用Map来存储。 -
UNKNOWN_TYPE_HANDLER
:未知类型对象的TypeHandler。 -
ALL_TYPE_HANDLERS_MAP
:录了全部TypeHandler的类型以及该类型相应的TypeHandler对象。
三、getInstance
【功能】根据传入的类型处理器类型和指定处理的Java类型,创建类型处理器实例对象。
【源码与注解】
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
// 如果传入的Java类型非空,则使用处理器类型的含参构造器创建处理器
if (javaTypeClass != null) {
try {
Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
return (TypeHandler<T>) c.newInstance(javaTypeClass);
} catch (NoSuchMethodException ignored) {
// ignored
} catch (Exception e) {
throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
}
}
// 如果传入的Java类型为空,则使用处理器类型的默认构造器创建处理器
try {
Constructor<?> c = typeHandlerClass.getConstructor();
return (TypeHandler<T>) c.newInstance();
} catch (Exception e) {
throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
}
}
四、处理器注册
TypeHandlerRegistry
中主要有两类方法,其中一类就是根据各种入参注册处理器,类中有很多 #registry
方法的重载实现,其调用关系如下图所示:

从图中可以看出除了方法 ⑤ ,最终都会调用到方法④ ,所以先介绍方法④ ,源码如下:
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
// (1)添加 handler 到 TYPE_HANDLER_MAP 中
if (javaType != null) {
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null) {
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
}
// (2)添加 handler 到 ALL_TYPE_HANDLERS_MAP
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
【解析】
- (1)若传入的 Java 类型非空,考虑到可能该 Java 类型已经注册了跟某个 JdbcType 映射处理时用到的处理器,所以先尝试从
TYPE_HANDLER_MAP
集合中找到对应的映射map,若为空表示第一次注册与该 Java 类型处理相关的处理器,则先创建map,再添加到 TYPE_HANDLER_MAP` 集合中。 - (2)若传入的 Java 类型为空,则只注册 handler 到
ALL_TYPE_HANDLERS_MAP
集合中。
⑥ register(String packageName)
【功能】通过包名,批量注册类型处理器,当在 mybatis-config.xml 通过包扫描的方式注册处理器时就会调用本方法处理。
【源码与注解】
public void register(String packageName) {
// 解析器工具类: 从指定包名中解析出包中的类型处理器
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) {
//Ignore inner classes and interfaces (including package-info.java) and abstract classes
// 过滤掉内部类、接口以及抽象类,调用 #register(Class<?> typeHandlerClass) 注册
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
register(type);
}
}
}
【解析】
ResolverUtil
是一个用来解析包中指定类型的工具类,解析后会保存到其对象内部的一个集合中,这里解析完之后对该集合进行了遍历,循环体内先过滤掉内部类、接口和抽象类,然后调用 #register(Class<?> typeHandlerClass)
方法处理。
① register(Class<?> typeHandlerClass)
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
// (1)获得 @MappedTypes 注解
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> javaTypeClass : mappedTypes.value()) {
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
// (2)未使用 @MappedTypes 注解,直接使用
if (!mappedTypeFound) {
register(getInstance(null, typeHandlerClass));
}
}
【解析】
- (1)如果使用了
@MappedTypes
注解,则解析注解中指定的 Java 类型,调用#register(Class<?> javaTypeClass, Class<?> typeHandlerClass)
注册处理器,其源码如下:
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}
-
(1.1)先创建对应的处理器实例,再调用
#register(Type javaType, TypeHandler<? extends T> typeHandler)
处理,注册指定 JavaType 的指定 TypeHandler 对象。 -
(2)若未使用该注解,则调用
#register(TypeHandler<T> typeHandler)
方法 ② 直接注册。
② register(TypeHandler<T> typeHandler)
public <T> void register(TypeHandler<T> typeHandler) {
boolean mappedTypeFound = false;
// (1)获得 @MappedTypes 注解
MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
// (2)优先使用 @MappedTypes 注解的 JavaType 进行注册
if (mappedTypes != null) {
for (Class<?> handledType : mappedTypes.value()) {
register(handledType, typeHandler);
mappedTypeFound = true;
}
}
// (3)其次,当 typeHandler 为 TypeReference 子类时,解析其中的 JavaType 进行注册
// @since 3.1.0 - try to auto-discover the mapped type
if (!mappedTypeFound && typeHandler instanceof TypeReference) {
try {
TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
register(typeReference.getRawType(), typeHandler);
mappedTypeFound = true;
} catch (Throwable t) {
// maybe users define the TypeReference with a different type and are not assignable, so just ignore it
}
}
// (4)最差,使用 JavaType 为 null 进行注册
if (!mappedTypeFound) {
register((Class<T>) null, typeHandler);
}
}
【解析】
- (1)获得 @MappedTypes 注解。
- (2)优先使用 @MappedTypes 注解的 JavaType 进行注册。
- (3)其次,当 typeHandler 为 TypeReference 子类时,解析其中的 JavaType 进行注册。
- (4)最差,使用 JavaType 为 null 进行注册。
register(Class<T> javaType, TypeHandler<? extends T> typeHandler)
③ register(Type javaType, TypeHandler<? extends T> typeHandler)
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
register((Type) javaType, typeHandler);
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
// (1)从处理器定义中获取 @MappedJdbcTypes 的标注
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
// (1.1)解析注解中定义的 JdbcType 并注册
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
// (1.2)注解中还声明要注册一个 null 的 JdbcType
if (mappedJdbcTypes.includeNullJdbcType()) {
register(javaType, null, typeHandler);
}
} else {
// (2)没有解析到 JdbcType,直接注册一个 null 的 JdbcType
register(javaType, null, typeHandler);
}
}
【解析】
方法① 中的两个分支,最后都会调用 #register(Class<T>, TypeHandler)
,该方法中将 Java 类型的 Class 对象向上转型为 Type 对象,再转给 #register(Type, TypeHandler)
方法处理,在该方法中:
- (1)优先使用
@MappedJdbcTypes
。- (1.1)解析注解中定义的 JdbcType 并注册。
- (1.2)注解指定要注册一个 JdbcType 为空的注册信息。
- (2)没有使用注解,JdbcType 默认为空。
- (3)上述两个分支都是调用方法④ 。
上述的方法除了方法④ 都是入参没明确指定 Java 类型和 Jdbc 类型,下面这两个方法,对应调用关系图的右侧分支,就直接指定了。
register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass)
register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler)
【源码与注解】
public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
// 调用 getInstance() 创建处理器对象
register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
}
public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
// 将 java Class 向上转型为 java Type
register((Type) type, jdbcType, handler);
}
【解析】
将 java Class 向上转型为 java Type,再调用方法④ 。
在右侧分支,还有两个方法。
register(String javaTypeClassName, String typeHandlerClassName)
public void register(String javaTypeClassName, String typeHandlerClassName) throws ClassNotFoundException {
register(Resources.classForName(javaTypeClassName), Resources.classForName(typeHandlerClassName));
}
【解析】
传入的是 Java 类型和处理器类型的类权限定名,调用 Resources.classForName
先加载对应的 Class 对象,再调用 #register(Class, Class)
方法。
register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler)
public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
register(javaTypeReference.getRawType(), handler);
}
【解析】
该方法主要是获取 TypeReference 中的泛型类型,然后调用 #register(Type, TypeHandler)
方法处理。
最后在调用关系图中,还有一个独立的方法⑤。
⑤ register(JdbcType jdbcType, TypeHandler<?> handler)
public void register(JdbcType jdbcType, TypeHandler<?> handler) {
JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
}
【解析】很简单,直接注册到 JDBC_TYPE_HANDLER_MAP
集合中。
最后总结一下:
(1)如果使用包扫描注册用户自定义处理器,需要配合使用注解
@MappedTypes
、@MappedJdbcTypes
,分别指定要注册的 Java 类型和 Jdbc 类型。
- 若只用了
@MappedTypes
,则注册进去的 Java 类型为 null- 若只用了
@MappedJdbcTypes
,则注册进去的 Jdbc 类型为 null整个调用关系图的左侧和中间的方法,都依赖于这两个注解,不过都不常用。
(2)如果指定了 Java 类型和 Jdbc 类型,则使用右侧的两个方法。
上述的(1)(2)最终都会填充
TYPE_HANDLER_MAP
和ALL_TYPE_HANDLERS_MAP
集合,(3)独立的方法⑤,专门用来注册填充
JDBC_TYPE_HANDLER_MAP
集合。
五、查找处理器
TypeHandlerRegistry
另一类重要的方法就是查找处理器的 #getTypeHandler()
方法,同样该方法也提供了丰富的重载实现,其调用关系如下图所示:

从调用关系图中可看出,方法 ① 是比较重要的核心方法,有三种方式查找处理器,都要调用到方法 ①,所以先介绍该方法的实现,源码如下:
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
// 忽略 ParamMap 的情况
if (ParamMap.class.equals(type)) {
return null;
}
// (1) 查找 java 类型对应的 jdbc 类型和处理器的集合
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
// (2.1) 优先根据 jdbcType 获取对应的 handler
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
// (2.2) 其次,使用 null 获取对应的 TypeHandler ,可以认为是默认的 TypeHandler
handler = jdbcHandlerMap.get(null);
}
// (2.3) 最差,从 TypeHandler 集合中选择一个唯一的 TypeHandler
if (handler == null) {
// #591
handler = pickSoleHandler(jdbcHandlerMap);
}
}
// (3) 若经过上面处理还没找到对应的 handler,且 javaType 是一个枚举类型,则返回默认的 EnumTypeHandler
if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
handler = new EnumTypeHandler((Class<?>) type);
}
// type drives generics here
return (TypeHandler<T>) handler;
}
【解析】
- (1)查找 java 类型对应的 jdbc 类型和处理器映射Map。
- (2)若该 Map 不为空
- (2.1)优先根据 jdbcType 获取对应的 handler。
- (2.2)其次,使用 null 获取对应的 TypeHandler ,可以认为是默认的 TypeHandler。
- (2.3)最差,从 TypeHandler 集合中选择一个唯一的 TypeHandler,调用
#pickSoleHandler()
方法实现,获得该 Map 中唯一的 TypeHandler,该方法源码如下:
private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
TypeHandler<?> soleHandler = null;
// 执行到这里 jdbcHandlerMap 肯定不为空,若该Map中若存在多个映射,则返回空,否则返回唯一的 handler
for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
if (soleHandler == null) {
soleHandler = handler;
} else if (!handler.getClass().equals(soleHandler.getClass())) {
// More than one type handlers registered.
return null;
}
}
return soleHandler;
}
- (3)若 java 类型是枚举类型,且经过上述步骤没找到 handler,则返回处理枚举类型默认的
EnumTypeHandler
。
调用关系图中的第一种情况
TypeHandler<T> getTypeHandler(Class<T> type)
public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
// 指定 jdbcType 为 null,调用方法 ①
return getTypeHandler((Type) type, null);
}
调用关系图中的第二种情况
TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType)
public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
// 将 java Class 转成 java type
return getTypeHandler((Type) type, jdbcType);
}
调用关系图中的第三种情况
public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
return getTypeHandler(javaTypeReference, null);
}
public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
// 获取 TypeReference 中的泛型类型作为 java 类型
return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
}
六、其他方法
比较简单,都是根据参数判断注册表中是否有处理器。
public boolean hasTypeHandler(Class<?> javaType) {
return hasTypeHandler(javaType, null);
}
public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
return hasTypeHandler(javaTypeReference, null);
}
public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
}
public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
}
public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
return ALL_TYPE_HANDLERS_MAP.get(handlerType);
}
public TypeHandler<Object> getUnknownTypeHandler() {
return UNKNOWN_TYPE_HANDLER;
}
七、内置初始化注册表
MyBatis 在初始化时会调用 TypeHandlerRegistry
的构造方法创建一个类型转换处理器注册表,该构造方法如下:
public TypeHandlerRegistry() {
// 不指定 jdbcType,会注册一个 null
// <JavaType, <JdbcType, TypeHandler>>
// <Boolean, <null, BooleanTypeHandler>
// 这样在做参数绑定时,若 java 类型为 Boolean,则只会调用唯一的 BooleanTypeHandler 处理
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
// 填充 JDBC_TYPE_HANDLER_MAP
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
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());
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(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
// jsr310类型处理标准,针对一些新的类型增添了一些对应的处理器,需要引入依赖
// mybatis-typehandlers-jsr310
try {
// since 1.0.0
register("java.time.Instant", "org.apache.ibatis.type.InstantTypeHandler");
register("java.time.LocalDateTime", "org.apache.ibatis.type.LocalDateTimeTypeHandler");
register("java.time.LocalDate", "org.apache.ibatis.type.LocalDateTypeHandler");
register("java.time.LocalTime", "org.apache.ibatis.type.LocalTimeTypeHandler");
register("java.time.OffsetDateTime", "org.apache.ibatis.type.OffsetDateTimeTypeHandler");
register("java.time.OffsetTime", "org.apache.ibatis.type.OffsetTimeTypeHandler");
register("java.time.ZonedDateTime", "org.apache.ibatis.type.ZonedDateTimeTypeHandler");
// since 1.0.1
register("java.time.Month", "org.apache.ibatis.type.MonthTypeHandler");
register("java.time.Year", "org.apache.ibatis.type.YearTypeHandler");
} catch (ClassNotFoundException e) {
// no JSR-310 handlers
}
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
网友评论