Mybatis源码分析系列之配置文件加载(二)
也许文中的一些说明能解决你的困惑。也许有表述不清晰的地方。请大家多多指点。欢迎下方留言。本人准备系统的总结一下各种框架的一些源码。也希望大家能给我提出宝贵意见
我们今天继续上文 传送门:Mybatis源码分析系列之配置文件加载(一)
2.11 typeHandlerElement
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
我们看名字可以理解这个配置文件的含义。在我们日常生产生活中。经常会遇到枚举类。我们在数据库中可能只存放1234这样的标识。但是生产中 我们需要得到标识对应的名称。我们就需要自己定义这个类型处理器来实现。
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
<!-- 下面是package配置-->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
<!--处理枚举类型的数据 -->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//取得package标签中的name
String typeHandlerPackage = child.getStringAttribute("name");
//通过register 把 package 全部注册到类型注册器中
typeHandlerRegistry.register(typeHandlerPackage);
} else {
//除了package之外的走下面
/**
*获得javaType|jdbcType|handler
*/
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
//解析属性值
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
//把这些注册到类型注册中心
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
2.11.1 register(String) 方法
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
//其实这块代码真的没什么可说的。大家可以参考(一中我已经写过这块的分析了。)
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
register(type);
}
}
}
2.11.2 register(Class<?> typeHandlerClass)
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
//获得注解。注解有几种 我们这列举一下
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> javaTypeClass : mappedTypes.value()) {
//进行注册
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
if (!mappedTypeFound) {
//没有注解。也就是没有找到 指定与其关联的 Java 类型列表
register(getInstance(null, typeHandlerClass));
}
}
上面获得注解其实就是@MappedTypes 注解
上面我们说的举例。我就引用官网的一些说明了 1
通过类型处理器的泛型,MyBatis 可以得知该类型处理器处理的 Java 类型,不过这种行为可以通过两种方法改变:
在类型处理器的配置元素(typeHandler element)上增加一个 javaType 属性(比如:javaType="String");
在类型处理器的类上(TypeHandler class)增加一个 @MappedTypes 注解来指定与其关联的 Java 类型列表。 如果在 javaType 属性中也同时指定,则注解方式将被忽略。
可以通过两种方式来指定被关联的 JDBC 类型:
在类型处理器的配置元素上增加一个 jdbcType 属性(比如:jdbcType="VARCHAR");
在类型处理器的类上(TypeHandler class)增加一个 @MappedJdbcTypes 注解来指定与其关联的 JDBC 类型列表。 如果在 jdbcType 属性中也同时指定,则注解方式将被忽略。
当决定在ResultMap中使用某一TypeHandler时,此时java类型是已知的(从结果类型中获得),但是JDBC类型是未知的。 因此Mybatis使用javaType=[TheJavaType], jdbcType=null的组合来选择一个TypeHandler。 这意味着使用@MappedJdbcTypes注解可以限制TypeHandler的范围,同时除非显式的设置,否则TypeHandler在ResultMap中将是无效的。 如果希望在ResultMap中使用TypeHandler,那么设置@MappedJdbcTypes注解的includeNullJdbcType=true即可。 然而从Mybatis 3.4.0开始,如果只有一个注册的TypeHandler来处理Java类型,那么它将是ResultMap使用Java类型时的默认值(即使没有includeNullJdbcType=true)。
2.11.3register(Class<?> javaTypeClass, Class<?> typeHandlerClass)
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
//调用2.11.4
//说明:getInstance 先通过 getConstructor 获得Constructor 构造器 在通过newInstance 获得 typeHandler实例
//getInstance其实就是获得对应java属性的 typeHandler
register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}
2.11.4 register(Class javaType, TypeHandler<? extends T> typeHandler)
源码其实就是一层一层的封装调用。看起来都是方法名称重载。
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
register((Type) javaType, typeHandler);
}
2.11.5 register(Type javaType, TypeHandler<? extends T> typeHandler)
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//重要重要。看到没有需要获得注解了。这次获得注解是啥呢。 @MappedJdbcTypes 就是这个注解了
// 注解来指定与其关联的 JDBC 类型列表。
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
//发现发现。出现一个新东西。在上面我们提到过。如果希望在ResultMap中使用TypeHandler,那么设置@MappedJdbcTypes注解的includeNullJdbcType=true即可
if (mappedJdbcTypes.includeNullJdbcType()) {
register(javaType, null, typeHandler);
}
} else {
register(javaType, null, typeHandler);
}
}
2.11.6 register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
//通过javaType可以获得jdbcType与TypeHandler的映射关系
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
//新建一个映射关系
map = new HashMap<JdbcType, TypeHandler<?>>();
//平且与java类型做映射
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
}
//存储所有的TypeHandler
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
我们已经把所有的注册方法全部写出来了。。其实核心就是要搞清楚java类型与jdbc类型以及typeHandler的关系而已。。产生对应的映射。仅此而已。但是源码就是源码。所有方面都考虑进来了。