[MyBatis源码分析 - 类型模块 - 组件二] TypeHandlerRegistry
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[MyBatis源码分析 - 类型模块 - 组件二] TypeHandlerRegistry相关的知识,希望对你有一定的参考价值。
参考技术A TypeHandlerRegistry 类型处理器注册表注册了类型转换时需要用到的各种处理器以及与Java类型和Jdbc类型的映射关系。【功能】根据传入的类型处理器类型和指定处理的Java类型,创建类型处理器实例对象。
【源码与注解】
TypeHandlerRegistry 中主要有两类方法,其中一类就是根据各种入参注册处理器,类中有很多 #registry 方法的重载实现,其调用关系如下图所示:
【解析】
【功能】通过包名,批量注册类型处理器,当在 mybatis-config.xml 通过包扫描的方式注册处理器时就会调用本方法处理。
【源码与注解】
【解析】
ResolverUtil 是一个用来解析包中指定类型的工具类,解析后会保存到其对象内部的一个集合中,这里解析完之后对该集合进行了遍历,循环体内先过滤掉内部类、接口和抽象类,然后调用 #register(Class<?> typeHandlerClass) 方法处理。
【解析】
【解析】
【解析】
方法① 中的两个分支,最后都会调用 #register(Class<T>, TypeHandler) ,该方法中将 Java 类型的 Class 对象向上转型为 Type 对象,再转给 #register(Type, TypeHandler) 方法处理,在该方法中:
上述的方法除了方法④ 都是入参没明确指定 Java 类型和 Jdbc 类型,下面这两个方法,对应调用关系图的右侧分支,就直接指定了。
【源码与注解】
【解析】
将 java Class 向上转型为 java Type,再调用方法④ 。
在右侧分支,还有两个方法。
【解析】
传入的是 Java 类型和处理器类型的类权限定名,调用 Resources.classForName 先加载对应的 Class 对象,再调用 #register(Class, Class) 方法。
【解析】
该方法主要是获取 TypeReference 中的泛型类型,然后调用 #register(Type, TypeHandler) 方法处理。
最后在调用关系图中,还有一个独立的方法⑤。
【解析】很简单,直接注册到 JDBC_TYPE_HANDLER_MAP 集合中。
TypeHandlerRegistry 另一类重要的方法就是查找处理器的 #getTypeHandler() 方法,同样该方法也提供了丰富的重载实现,其调用关系如下图所示:
【解析】
调用关系图中的第一种情况
调用关系图中的第二种情况
调用关系图中的第三种情况
比较简单,都是根据参数判断注册表中是否有处理器。
MyBatis 在初始化时会调用 TypeHandlerRegistry 的构造方法创建一个类型转换处理器注册表,该构造方法如下:
[MyBatis源码分析 - 反射器模块 - 组件五] Property 工具集
一、简介
??在 org.apache.ibatis.reflection
包中,有个 property 的目录,都是关于属性操作的工具类,分别是 PropertyCopier
、PropertyNamer
、PropertyTokenizer
,PropertyCopier 是属性拷贝的工具类,PropertyNamer 是将根据方法名获取属性名的工具类,PropertyTokenizer 是定位属性表达式中属性的工具类。
二、PropertyCopier
1、功能
??将一个JavaBean对象的所有属性值,复制给另一个相同类型的bean。
2、源码与注解
public final class PropertyCopier {
private PropertyCopier() {
// Prevent Instantiation of Static Class
}
/**
*
* @param type JavaBean对象类型
* @param sourceBean 属性拷贝的源对象
* @param destinationBean 属性拷贝的目标对象
*/
public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
Class<?> parent = type;
// 当上溯还有父类时,继续循环处理找到父类中可能定义的属性
while (parent != null) {
// 获取类中定义的所有属性
final Field[] fields = parent.getDeclaredFields();
// 遍历属性并通过反射赋值
for(Field field : fields) {
try {
field.setAccessible(true);
field.set(destinationBean, field.get(sourceBean));
} catch (Exception e) {
// Nothing useful to do, will only fail on final fields, which will be ignored.
}
}
// 类拥有的属性可能从基类中继承而来,所以上溯父类,继续循环
parent = parent.getSuperclass();
}
}
}
??该类是一个工具类,对外暴露 #copyBeanProperties()
的静态方法,为了防止调用代码实例化该类的对象,将构造方法声明为私有。
三、PropertyNamer
1、功能
??围绕方法名判断是否 getter/setter 方法,提取属性名。
2、源码与注解
public final class PropertyNamer {
private PropertyNamer() {
// Prevent Instantiation of Static Class
}
// 根据getter/setter方法名提取对应的属性名
public static String methodToProperty(String name) {
// 如果方法名形如isXxx,则先提取出Xxx
if (name.startsWith("is")) {
name = name.substring(2);
// 如果方法名形如getYyy、setZzz,则先提出出Yyy、Zzz
} else if (name.startsWith("get") || name.startsWith("set")) {
name = name.substring(3);
// 如果方法名不符合上面的形式,则抛出异常
} else {
throw new ReflectionException("Error parsing property name ‘" + name + "‘. Didn‘t start with ‘is‘, ‘get‘ or ‘set‘.");
}
// 将上面拿到的Xxx、Yyy、Zzz转化为xxx、yyy、zzz,注意只处理首字母转化为小写
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
return name;
}
// 根据方法名判断是否为一个getter/setter方法
public static boolean isProperty(String name) {
return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
}
// 根据方法名判断是否为一个getter方法,一般其方法名为getXxx、isYyy (如果属性类型为布尔型)
public static boolean isGetter(String name) {
return name.startsWith("get") || name.startsWith("is");
}
// 根据方法名判断是否为一个setter方法,一般其方法名为setZzz
public static boolean isSetter(String name) {
return name.startsWith("set");
}
}
三、PropertyTokenizer
1、功能
??解析属性表达式,比如结果映射或者执行SQL传参,<resultMap> 的 property 属性或 sql 元素中的参数变量占位 #{}
中的内容都可能是一个属性表达式,要正确得设置返回结果对象或者从传入的参数中找到对应的属性值替换占位,都需要先正确地解析属性表达式。
??比如一个商品交易订单中可能包含多件商品,一对多关系,当根据商品订单号查到多件商品goods
,但是结果映射转化为对象时,只需要某件商品的某个属性信息,比如第一件商品的价格,则用类似 goods[0].price
这样的属性表达式来表达,当然假如商品的可以有更复杂的属性比如交易信息 tradeInfo
,交易时间的属性表达式就为 goods[0].tradeInfo.trxTime
,所以 PropertyTokenizer
解析的就是这种带 .
和 []
的表达式。
2、成员属性
private String name; // 当前表达式的名称
private String indexedName; // 当前表达式的索引名
private String index; // 索引下标
private String children; // 子表达式
name
:表达式最顶层的属性名。indexedName
:第一个.
前的表达式内容。index
:索引下标。children
:.
后面的子表达式。
??比如表达式 goods[0].price
,indexedName 为 goods[0]
,name 为 goods
,index 为 0
,children 为 price
。
3、源码与注解
public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> {
private String name; // 当前表达式的名称
private String indexedName; // 当前表达式的索引名
private String index; // 索引下标
private String children; // 子表达式
public PropertyTokenizer(String fullname) {
// 以第一个‘.‘为界限,找到前半部分和子表达式
int delim = fullname.indexOf(‘.‘);
if (delim > -1) {
name = fullname.substring(0, delim);
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
// 从前半部分中,解析出表达式名和索引
indexedName = name;
delim = name.indexOf(‘[‘);
if (delim > -1) {
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
public String getName() {
return name;
}
public String getIndex() {
return index;
}
public String getIndexedName() {
return indexedName;
}
public String getChildren() {
return children;
}
// 实现接口定义的方法,使对象可迭代
@Override
public boolean hasNext() {
return children != null;
}
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
}
@Override
public Iterator<PropertyTokenizer> iterator() {
return this;
}
}
以上是关于[MyBatis源码分析 - 类型模块 - 组件二] TypeHandlerRegistry的主要内容,如果未能解决你的问题,请参考以下文章
MyBatis源码分析-基础支持层反射模块Reflector/ReflectorFactory
浩哥解析MyBatis源码——Parsing解析模块之通用标记解析器(GenericTokenParser)与标记处理器(TokenHandler)