Mybatis 源码学习-反射工具(Property 工具)
Posted 凉茶方便面
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 源码学习-反射工具(Property 工具)相关的知识,希望对你有一定的参考价值。
历史文章:
Mybatis 源码学习(4)-反射工具(ObjectFactory)
Property 工具集包含 PropertyTokenizer
、PropertyNamer
、PropertyCopier
三个工具类,其中 PropertyTokenizer
用于符号解析,PropertyNamer
用于解析和判断属性名称,PropertyCopier
用于对象属性复制。
PropertyTokenizer
PropertyTokenizer 用于解析属性占位符表达式,如,将字符串 orders[0].items[0].name
解析为实际的对象关系,需要将属性和下标以及在属性之间做出分割。
public class PropertyTokenizer implements Iterator<PropertyTokenizer>
private String name; // 当前表达式的名称
private final String indexedName; // 当前表达式带索引的名称
private String index; // 索引下标
private final 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; // indexedName 赋值,默认是带下标的字符串,但是如果已完全解析,就只有属性名
delim = name.indexOf('['); // 下标的开始字符位置
if (delim > -1)
index = name.substring(delim + 1, name.length() - 1); // 解析下标(从 delim 到最后,没有 trim 操作,所有要求编写时不能带空格)
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;
public boolean hasNext()
return children != null;
public PropertyTokenizer next()
return new PropertyTokenizer(children);
public void remove()
throw new UnsupportedOperationException(“…”);
初始化 PropertyTokenizer 时,就会自动解析第一个属性,PropertyTokenizer.next 方法会继续解析后边的属性,它会利用 .
分割符,为属性分组,通过[
字符,分割属性名称和属性下标。如解析属性表达式 orderx[0].items[0].name
时,其过程如下:
PropertyNamer
PropertyNamer 有四个方法:methodToProperty
、isProperty
、isGetter
、isSetter
,主要用于方法名到属性名的转换,还有方法类型的判断。
public final class PropertyNamer
// 防止实例化该对象
private PropertyNamer()
// 将方法转化为属性名称(即,即使只有set/get方法,没有对应字段也能够解析为属性)
// 通过前缀判断
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'.");
// 检查除去前缀后的名称,如果前缀后的第一个字符是大写,则改为小写
// 这个方法有点儿粗暴,像是 island 这样的单词作为方法名,就会被分割为 land,会不符合预期
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;
// 是否是属性字段(通过前缀判断)
public static boolean isProperty(String name)
return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
// 判断是否是 get 方法(is 对应 boolean 方法)
public static boolean isGetter(String name)
return name.startsWith("get") || name.startsWith("is");
// 是否是 set 方法
public static boolean isSetter(String name)
return name.startsWith("set");
PropertyCopier
PropertyCopier 是属性拷贝的工具类,它可以在两个相同类型的对象间拷贝内容,它只有一个方法:copyBeanProperties
。
public final class PropertyCopier
// 防止实例化该对象
private PropertyCopier()
public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean)
Class<?> parent = type;
while (parent != null)
// 获取所有的 field
final Field[] fields = parent.getDeclaredFields();
for(Field field : fields)
try
// 按照继承关系,将属性按照名称逐级从 sourceBean 设置到 destinationBean 对象中
field.setAccessible(true);
field.set(destinationBean, field.get(sourceBean));
catch (Exception e)
parent = parent.getSuperclass(); // 继续设置父类中的字段
总结
Property 工具主要的功能就是解析对象属性,如 PropertyTokenizer 是为了解析 xml 文件中的对象占位符,PropertyNamer 是为了处理对象中的方法到属性之间的映射和判断,PropertyCopier 是两个具有继承关系的对象之前进行属性赋值。
参考文档:《Mybatis 技术内幕》
本文的基本脉络参考自《Mybatis 技术内幕》,编写文章的原因是希望能够系统地学习 Mybatis 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。
欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。
以上是关于Mybatis 源码学习-反射工具(Property 工具)的主要内容,如果未能解决你的问题,请参考以下文章
Mybatis 源码学习-反射工具(TypeParameterResolver)
Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)