回顾
上一篇分析了MyBatis如何对泛型进行解析还有如何去利用ObjectFactory去创建对象
Property工具集
Property工具集主要有三个
- PropertyTokenizer
- PropertyNamer
- PropertyCopier
这三个工具集是用来解析SQL映射配置文件中的ResultMap标签的表达式的
比如有如下的ResultMap
<resultMap>
<result property = "order[0].items[0].name" column = "item1"/>
<result property = "order[0].items[1].name" column = "item2"/>
</resultMap>
这个ResultMap的映射结果就是将item1的列的值,会变成第一个订单的第一个item的name属性,而item2会变成第一个订单的第二个item的name属性
这种由".“和”[]"组成的表达式是由PropertyTokenizer来负责解析的
PropertyTokenizer
可以看到,这个PropertyTokenizer是属于反射模块里面的
而且从构造方法里面就进行解析了
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) {
//如果存在,那么就说明第一级的最后面肯定是]
//然后取出delim索引前面的,到倒数第二个的字符串
//该字符串就代表为索引了
index = name.substring(delim + 1, name.length() - 1);
//此时第一级就变成了索引前面了
name = name.substring(0, delim);
}
}
可以看到,PropertyTokenizer负责的是解析order[0].item这种的字符串
- 获取点分隔符的位置,判断有没有点分隔符
- 如果有点分隔符就证明有两层
- 根据点分隔符划分出了name和children,name就是order[0],children就是item
- 如果没有分隔符,那么整个待解析的字符串就为name,children为null
- 如果有点分隔符就证明有两层
- 接下来就要解析是否有索引了,也就是【0】
- 判断左方括号有没有,如果有就证明肯定有索引,此时直接左方括号的下一个位置到倒数第二个位置,代表的就是索引值,倒数最后一个为右方括号,此时解析出来的index就是0
- 同时name也要进行重新划分了,name变成了开头到左方括号的前一位,也就是order
可能有人觉得那么对于order[0].item[0].name又怎么进行解析呢?
其实这就是一个简单的递归而已,划分出了children为item[0].name,此时对item进行同样的拆分解析即可
PropertyNamer
PropertyNamer也是位于反射模块下面的,其主要的功能就是完成方法名到属性名的转换(其实前面我们在反射模块已经看到了,如何将Get\Set方法名转换成变量名)
public static String methodToProperty(String name) {
if (name.startsWith("is")) {
name = name.substring(2);
} 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'.");
}
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;
}
这里就不再赘述了
PropertyCopier
PropertyCopier也是反射模块下的,其负责对属性进行拷贝,里面只有一个静态方法
源码如下
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 {
try {
//执行destinationBean的set方法,并且参数为复制对象里面的属性值
//使用get方法获得复制对象里面的属性值,然后执行目标对象的set方法注入属性值
field.set(destinationBean, field.get(sourceBean));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
field.set(destinationBean, field.get(sourceBean));
} else {
throw e;
}
}
} catch (Exception e) {
// Nothing useful to do, will only fail on final fields, which will be ignored.
}
}
//获取下一个父类
parent = parent.getSuperclass();
}
}
可以看到,这里其实就是循环遍历了所有的父类、并且通过反射获取所有的属性,执行get方法获取到复制对象里面对应的属性值,再执行set方法注入到新生成的对象里面
MetaClass
MetaClass是用于对复杂的属性表达式进行解析的,其利用的就是前面我们学习过的Reflector和PropertyTokenizer(Reflector用于存储类的元信息,PropertyTokenizer用于解析字符串)
其中里面比较重要的是一个findProperty方法,该方法就是用来解析复杂的表达式的
可以看到,该方法主要是由buildProperty来实现的,并且可以看到,这里还会针对useCamelCaseMapping来忽略了下划线,这个useCamelCaseMapping其实就是是否开启了驼峰命名,所以大概可以估计到,MetaClass其实是根据数据库的列名来转化成属性名的
下面就来看看
buildProperty
该方法的源码如下
private StringBuilder buildProperty(String name, StringBuilder builder) {
//对name进行解析
PropertyTokenizer prop = new PropertyTokenizer(name);
//判断解析过后有没有Children,也就是有没有子表达式
//判断是否解析完全了
if (prop.hasNext()) {
//如果有二级,也就是还有Children子表达式
//使用reflector去获取name对应的属性
//name为目标类里面的一个成员属性名称
//findPropertyName是从caseInsensitivePropertyMap集合里面去取
//并且里面的是有get或者有set方法的属性
String propertyName = reflector.findPropertyName(prop.getName());
//如果可以找到对应的属性
if (propertyName != null) {
//builder重新组合
builder.append(propertyName);
builder.append(".");
//为该属性创建对应的MetaClass对象
//因为下面我们就要从另外一个属性的Reflector里面去解析children了
//children是属于另外一个属性的,比如User类里面的order.item
//现在我们从User里面解析了order,但要解析item的时候要去order里面解析
MetaClass metaProp = metaClassForProperty(propertyName);
//递归继续去进行创建!!!这里就是解析Children了
metaProp.buildProperty(prop.getChildren(), builder);
}
} else {
//如果递归到没有Children,没有子标签
//就无序进行递归了
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}
可以看到metaClassForProperty只是根据了name去从Get方式里面获取返回值类型,然后去创建对应的MetaClass!!!
简单总结一下buildProperty
-
对传进来的name进行解析
- 判断Children是否为空
- 如果不为空,需j要对Children进行继续解析,使用name去创建出MetaClass,然后递归使用MetaClass再次对Children进行解析
- 如果为空,证明已经到底了,无需再对下级进行解析了
- 判断Children是否为空
-
而所谓的解析,仅仅只是拼接成name.children.children1的形式而已。。。。。。
-
而且这里要注意,使用name去创建出新的MetaClass是从Reflector的getTypes里面去获取name对应的Class类型,说白了就是该name的Get方法的返回值类型,所以如果没有Get方法这里是否报错的
hasGetter和hasSetter
hasGetter和hasSetter是用来获取属性是否有Getter方法和Setter方法的
todo
ObjectWrapper和ObjectWrapperFactory
ObjectWrapper
MetaClass是MyBatis对类级别的元信息的封装和处理,那么对于对象级别的呢?
MyBatis对于对象级别的元信息封装处理使用的是ObjectWrapper来进行处理的,ObjectWrapper接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息的方法,以及更新属性的方法
ObjectWrapper接口拥有如下的方法
-
get:调用对象对应属性的Get方法,如果对象是一个集合,则是获取该key的value
-
set:调用对象对应属性的set方法,如果对象是一个集合,则是设置对应Key的Value
-
findProperty:查找属性表达式指定的属性(是否忽略下划线来进行查找,既是否开启驼峰命名)
-
getGetterNames:查找可读属性的名称集合
-
getSetterNames:查找可写属性的名称集合
-
getSetterType:解析属性表达式指定属性的setter方法的参数类型
-
getGetterType:解析属性表达式指定属性的getter方法的返回值类型
-
hasSetter:是否有setter方法
-
hasGetter:是否有getter方法
-
isCollection:判断对象是不是集合
-
add:如果对象是集合,调用了对象的add方法
-
instantiatePropertyValue:根据属性表达式中的属性去创建相应的MetaObject对象
从该接口拥有的方法可以看到,ObjectWrapper其实就是提供了调用实例的一些方法,而且重点是对于属性的Getter和Setter方法,如果对象是一个集合,则是集合元素里面的读取和设置
从该ObjectWrapper里可以看出,其首先类有两个
- CollectionWrapper:实例为集合类型的
- BaseWrapper:实例为对象类型的,并且是一个抽象类
- BeanWrapper:对象为JavaBean类型
- MapWrapper:对象类型为Map接口实现类(Map不是属于Collection的!!!!)
CollectionWrapper
对于集合类型的CollectionWrapper
可以看到其组装了一个Collection集合,并且绝大对数的方法都是直接抛错没有实现的!!!只有add和addAll方法实现了
并且都是直接调用组装的Collection集合的
BaseWrapper
BaseWrapper是对于非集合类型的
可以看到,BaseWrapper是一个抽象类,并且其组装了一个MetaObject,并且没有重写ObjectWrapper里面的任何一个方法
下面就来简单介绍一下这三个方法
resolveCollection
该方法是用来解析属性表达式并获取指定的属性的
源码如下
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
//判断属性表达式的name属性是否为空
if ("".equals(prop.getName())) {
//如果为空
return object;
} else {
//如果不为空就调用metaObject的getValue方法
//该方法会解析name属性,获取name对应的值的
return metaObject.getValue(prop.getName());
}
}
getCollectionValue
这个方法是用来获取支持当对象为数组或者Map类型时,可以根据指定索引或者Key来获取对应的值的,当然,指定索引或者Key位于表达式里面,需要进行解析
源码如下
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
//Map类型处理
if (collection instanceof Map) {
return ((Map) collection).get(prop.getIndex());
}
//数组类型处理
else {
//获取索引
int i = Integer.parseInt(prop.getIndex());
//List处理
if (collection instanceof List) {
return ((List) collection).get(i);
}
//Object数组处理
else if (collection instanceof Object[]) {
return ((Object[]) collection)[i];
}
//八大基本数组类型
else if (collection instanceof char[]) {
return ((char[]) collection)[i];
} else if (collection instanceof boolean[]) {
return ((boolean[]) collection)[i];
} else if (collection instanceof byte[]) {
return ((byte[]) collection)[i];
} else if (collection instanceof double[]) {
return ((double[]) collection)[i];
} else if (collection instanceof float[]) {
return ((float[]) collection)[i];
} else if (collection instanceof int[]) {
return ((int[]) collection)[i];
} else if (collection instanceof long[]) {
return ((long[]) collection)[i];
} else if (collection instanceof short[]) {
return ((short[]) collection)[i];
} else {
throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
可以看到,对于数组的处理,其实就是把数组类型都枚举出来而已,List、Object数组和八大基本数据类型数组
setCollectionValue
有get就有set嘛,对应也会有setCollectionValue
源码如下
protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
//对Map的处理
if (collection instanceof Map) {
//调用Put方法
((Map) collection).put(prop.getIndex(), value);
}
//对数组的处理
else {
//获取索引
int i = Integer.parseInt(prop.getIndex());
//对List类型的处理
if (collection instanceof List) {
((List) collection).set(i, value);
}
//对Object数组的处理
else if (collection instanceof Object[]) {
((Object[]) collection)[i] = value;
}
//对八大基本数据类型数组的处理
else if (collection instanceof char[]) {
((char[]) collection)[i] = (Character) value;
} else if (collection instanceof boolean[]) {
((boolean[]) collection)[i] = (Boolean) value;
} else if (collection instanceof byte[]) {
((byte[]) collection)[i] = (Byte) value;
} else if (collection instanceof double[]) {
((double[]) collection)[i] = (Double) value;
} else if (collection instanceof float[]) {
((float[]) collection)[i] = (Float) value;
} else if (collection instanceof int[]) {
((int[]) collection)[i] = (Integer) value;
} else if (collection instanceof long[]) {
((long[]) collection)[i] = (Long) value;
} else if (collection instanceof short[]) {
((short[]) collection)[i] = (Short) value;
} else {
throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
可以看到,set方法也是一样去枚举处理而已,这里就不再赘述了
对于下面的BeanWrapper和MapWrapper,代码也很简单,这里就不再赘述了
ObjectWrapperFactory
ObjectWrapperFactory就是负责创建ObjectWrapper的
里面只有两个方法
- hasWrapperFor:判断指定的Object是否创建了ObjectWrapper
- getWrapperFor:使用指定的MetaObject去创建ObjectWrapper
其实现类仅仅只有一个DefaultObjectWrapperFactory
可以看到,其hasWrapperFor方法返回了false,并且getWrapperFor方法仅仅只是抛错,并且抛错的信息还说DefaultObjectWrapperFactory不能用于提供ObjectWrapper