Mybatis映射体系
MetaObject
MetaObject应用
查找属性:
忽略大小写,支持驼峰,支持子属性获取属性值:
基于点获取子属性,如user.name
基于索引获取属性列表值,如users[1].id
基于key获取map值,如user[name]设置属性
可设置子属性值
持自动创建子属性对象(必须有无参构造器,且不能是集合)
public class MetaObjectTest {
static class Address {
private String province;
private String city;
private String county;
private String addressDetail;
public String getProvince() { return province; }
public void setProvince(String province) { this.province = province; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getCounty() { return county; }
public void setCounty(String county) { this.county = county; }
public String getAddressDetail() { return addressDetail; }
public void setAddressDetail(String addressDetail) { this.addressDetail = addressDetail; }
}
private String name;
private Address address;
private List<Address> addressList;
private Map<String, Object> additionParams;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
public List<Address> getAddressList() { return addressList; }
public void setAddressList(List<Address> addressList) { this.addressList = addressList; }
public Map<String, Object> getAdditionParams() { return additionParams; }
public void setAdditionParams(Map<String, Object> additionParams) {
this.additionParams = additionParams;
}
public static void main(String[] args) {
Configuration configuration = new Configuration();
MetaObjectTest metaObjectTest = new MetaObjectTest();
// 使用MetaObject解析order对象
MetaObject metaobject = configuration.newMetaObject(metaObjectTest);
// 通过MetaObject设置对象属性值
metaObjectTest.setName("oldName");
System.out.println(metaObjectTest.getName());
metaobject.setValue("name", "newName");
// 通过输出结果,可以看到orderNo被MetaObject设置的值覆盖
System.out.println(metaObjectTest.getName());
System.out.println("--->");
// 通过MetaObject获取对象属性值
System.out.println("通过MetaObject获取对象属性值:" + metaobject.getValue("name"));
System.out.println("--->");
// 通过MetaObject设置嵌套属性的值,它会创建一个空的嵌套对象,并为其属性赋值
metaobject.setValue("address.addressDetail", "详细地址");
System.out.println("通过MetaObject自动创建内嵌对象,赋值之后获取 : "
+ metaobject.getValue("address.addressDetail"));
System.out.println("--->");
System.out.println("通过驼峰命名法获取属性名称 :"
+ metaobject.findProperty("address.address_detail", true));
System.out.println("--->");
// MetaObject无法自动创建List类型的内嵌对象,需要手动设置,如:
Address address = new Address();
address.setProvince("福建省");
List<Address> addressList = new ArrayList<>();
addressList.add(address);
metaobject.setValue("addressList", addressList);
metaobject.setValue("addressList[0]", address);
// 访问集合对象成员的方式(也可以访问成员的属性值)
System.out.println("通过MetaObject获取List类型成员属性 : "
+ metaobject.getValue("addressList[0].province"));
System.out.println("--->");
// MetaObject同样无法自动创建Map类型的内嵌对象,需要手动设置,如:
metaobject.setValue("additionParams", new HashMap<>());
// 设置map属性的键值对,以及访问键值
metaobject.setValue("additionParams[name]", "名称");
System.out.println("通过MetaObject获取Map类型成员属性 : "
+ metaobject.getValue("additionParams[name]"));
}
}
MetaObject源码解析
public class MetaObject {
// ...
// 以解析的属性名称name=comments[0].user.name为例
public Object getValue(String name) {
// 先通过属性分词器解析全属性名称
PropertyTokenizer prop = new PropertyTokenizer(name);
// 存在子属性user.name
if (prop.hasNext()) {
// 如果存在子属性,则通过IndexedName再次创建MetaObject
// IndexedName的值依次为: comments[0]、user
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
}
// 如果不存在子属性,比如解析到name属性时,不存在子属性,通过反射获取属性值(可参考下面BeanWrapper类源码)
else {
return objectWrapper.get(prop);
}
}
// 以解析comments[0].user.name为例
public MetaObject metaObjectForProperty(String name) {
// 这里会先后获取comments[0]、user两个属性的值
Object value = getValue(name);
// 将上面通过某个属性获取到的对象,继续将这些对象解析为MetaObject对象
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
PropertyTokenizer
属性名称分词器,它首先会根据"."符号,将属性名称拆分,比如comments[0].user.name通过多次的属性名称分词,会被划分为comments[0]、user、name三部分,再根据这三部分创建各自的MetaObject元对象,进一步解析子属性,源码如下:
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
private String name;
private final String indexedName;
// index之所以为String类型,是因为出了Collection是数值类型,还有Map类型采用String类型的key
private String index;
private final String children;
// 假定入参fullname属性全名为:comments[0].user.name
// 最终的解析结果为:
// name = comments
// indexedName = comments[0]
// index = 0
// children = user.name
public PropertyTokenizer(String fullname) {
// 首先,获取第一个.的偏移量
int delim = fullname.indexOf('.');
// 存在.的情况
if (delim > -1) {
// 先将name赋值为第一个.号之前的全部字符,这里为comments[0]
name = fullname.substring(0, delim);
// childre赋值为第一个.之后的全部字符串,这里为user.name
children = fullname.substring(delim + 1);
} else {
// 不存在.的字符串,直接将name赋值为整个入参fullname
name = fullname;
children = null;
}
// 将comments[0]赋值给indexedName
indexedName = name;
// 判断name是否存在[符号,存在的话将name赋值为[]符号外的字符串,这里将name重新赋值为comments
delim = name.indexOf('[');
if (delim > -1) {
// index赋值为[]内的下标数值,如果不存在[],index为默认值null
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
// ...
//hasNext判断是否存在子属性
public boolean hasNext() {
return children != null;
}
// 将子属性再次通过属性分词器分析
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
}
BeanWrapper
封装目标对象和目标对象类元数据,可以通过它反射获取属性值,这个类只能获取顶层属性的值,比如通过BeanWrapper
获取属性名称comments[0].user.name,它仅能返回comments[0]的值。
public class BeanWrapper extends BaseWrapper {
private final Object object;
private final MetaClass metaClass;
// ...
public Object get(PropertyTokenizer prop) {
// 通过属性分词器判断该属性是否是一个集合,如果属性分词器记录的index属性为null,表示当前要解析的属性不是集合
if (prop.getIndex() != null) {
// 继承父类BaseWrapper的方法(详见下方源码)
Object collection = resolveCollection(prop, object);
// 继承父类BaseWrapper的方法(详见下方源码)
return getCollectionValue(prop, collection);
} else {
// 解析非集合类型的属性值(详见下方源码)
return getBeanProperty(prop, object);
}
}
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
// 通过MetaClass获取属性值对应的MethodInvoker,内部封装了JDK反射的method对象(详见下方源码)
// 注意这里是通过属性分词器的name属性获取属性值
// 当尝试通过BeanWrapper获取属性值,且传入的属性为user.name时,这里的prop.getName()将会获取到user
//(参看上面PropertyTokenizer如何解析属性名称)
// 因此其实获取到的时user属性值,无法获取user对象的name属性值
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
// 通过反射获取属性值
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
// ...
}
}
}
MethodInvoker
封装了类的Setter和Getter,通过它类反射获取属性值或者设置属性值
public class MethodInvoker implements Invoker {
private final Class<?> type;
private final Method method;
public MethodInvoker(Method method) {
this.method = method;
// 兼容Getter和Setter方法,Setter根据入参类型决定,Getter根据出参类型决定
if (method.getParameterTypes().length == 1) {
type = method.getParameterTypes()[0];
} else {
type = method.getReturnType();
}
}
// 反射调用Getter或者Setter方法
public Object invoke(Object target, Object[] args)
throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, args);
}
// 返回当前操作的属性类型
public Class<?> getType() {
return type;
}
}
BaseWrapper
为接口ObjectWrapper
提供的封装了处理集合公共方法的抽象类
public abstract class BaseWrapper implements ObjectWrapper {
protected static final Object[] NO_ARGUMENTS = new Object[0];
protected final MetaObject metaObject;
protected BaseWrapper(MetaObject metaObject) {
this.metaObject = metaObject;
}
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
// 属性名称为空直接返回BeanWrapper封装的目标对象Object
if ("".equals(prop.getName())) {
return object;
} else {
// 若解析的属性是comments[0],将会返回comments属性名对应的对象值
return metaObject.getValue(prop.getName());
}
}
// 从集合中获取某个成员
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
// 若属性值为Map类型,PropertyTokenizer解析得到的index为key名称
if (collection instanceof Map) {
return ((Map) collection).get(prop.getIndex());
}
// 从List、数组类型取值
else {
int i = Integer.parseInt(prop.getIndex());
if (collection instanceof List) {
return ((List) collection).get(i);
} 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 {
// ...
}
}
}
// 为集合属性设置值
protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
// Map类型,以key为index值,value为入参value设置一个Map.Entry
if (collection instanceof Map) {
((Map) collection).put(prop.getIndex(), value);
}
// 为List、数组类型赋值
else {
int i = Integer.parseInt(prop.getIndex());
if (collection instanceof List) {
((List) collection).set(i, value);
} 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 {
// ...
}
}
}
}
Reflector
Reflector
可以解析一个类的所有属性,记录所有的Setter和Getter方法
public class Reflector {
// 解析的类
private final Class<?> type;
private final String[] readablePropertyNames;
private final String[] writeablePropertyNames;
// 通过类的Method解析
// 封装所有get开头的方法,key为去除get前缀,首字母小写的方法名称,value为封装Method的MethodInvoker
// 通过类的Field解析
// 封装所有属性的赋值方法,key为属性名称,value为属性的赋值方法SetFieldInvoker
private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
// 封装所有is、get开头的方法,key为去除is、get前缀,首字母小写的方法名称,value为封装Method的MethodInvoker
// 通过类的Field解析
// 封装所有属性的赋值方法,key为属性名称,value为属性的赋值方法GetFieldInvoker
private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
// 封装所有set开头,被认定为Setter方法的属性类型,以及所有Field的类型
// 如果是List<T>返回泛型类型,如果是T[]同样返回泛型
private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
// 封装所有is、get开头,被认定为Getter方法的属性类型,以及所有Filed的类型
// 如果是List<T>返回泛型类型,如果是T[]同样返回泛型
private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
// 解析得到的无参构造器
private Constructor<?> defaultConstructor;
// 保存所有可以取值、设置的属性名称的并集,key为属性名称全大写字符串,value为属性名称
private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();
public Reflector(Class<?> clazz) {
type = clazz;
// 获取无参构造器
addDefaultConstructor(clazz);
// 添加所有合法的Getter
addGetMethods(clazz);
// 添加所有合法的Setter
addSetMethods(clazz);
// 通过类的Field添加另外一批Getter和Setter方法
addFields(clazz);
// 使用数组保存上述通过Method和Filed解析得到的可以获取值的属性名称
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
// 使用数组保存上述通过Method和Filed解析得到的可以设置值的属性名称
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
// 将所有可以赋值、可以取值的属性名称,转换为大写字符串保存到一个Map中
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
// 获取默认无参构造器
private void addDefaultConstructor(Class<?> clazz) {
// 获取当前类型定义的全部构造器
Constructor<?>[] consts = clazz.getDeclaredConstructors();
// 遍历所有构造器,找到入参为0的构造器,并将其设置为可访问的
for (Constructor<?> constructor : consts) {
if (constructor.getParameterTypes().length == 0) {
if (canAccessPrivateMethods()) {
try {
constructor.setAccessible(true);
} catch (Exception e) {
}
}
if (constructor.isAccessible()) {
this.defaultConstructor = constructor;
}
}
}
}
private void addGetMethods(Class<?> cls) {
// 保存解析得到同名属性,却有多个方法的冲突Map
Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
// 获取cls(包括父类、接口定义的)上所有的方法
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
// Getter方法应该没有入参,不记录有参数的方法
if (method.getParameterTypes().length > 0) {
continue;
}
// 如果方法名称是get或者is为前缀,并且前缀之后还有字符,将后面的字符串作为属性名称
String name = method.getName();
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
// 删除方法名称前缀get、is、set,并将剩下的第一个字符变为小写
// 如:getUsername => username
name = PropertyNamer.methodToProperty(name);
// 保存得到的method,映射关系为 属性名 -> List<Method> ,避免同一个name获得多个方法
addMethodConflict(conflictingGetters, name, method);
}
}
// 为每个属性选定唯一一个Getter方法(方法详情略)
// 规则如下:
// 遍历每个方法的返回类型,
// 1. 存在返回类型相同的多个方法
// 1.1 如果不是boolean类型属性,存在多个方法返回类型一致,抛出异常
// 1.2 如果是boolean类型属性,以最后一个is开头的方法名称作为该属性的Getter方法
// 2. 如果多个Getter方法返回类型不一致,以最小的子类作为Getter方法
// 3. 解析得到的属性名称不能是$开头、serialVersionUID、class这三种形式的名称,不添加Getter方法
// 4. 将得到的Getter方法,封装为MethodInvoker对象
resolveGetterConflicts(conflictingGetters);
}
// ...
private void addSetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
// 获取cls(包括父类、接口定义的)上所有的方法
Method[] methods = getClassMethods(cls);
// 遍历所有以set开头,并且set之后还有字符的方法名称,且方法只有一个入参
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
// 删除方法名称前缀get、is、set,并将剩下的第一个字符变为小写
// 如:getUsername => username
name = PropertyNamer.methodToProperty(name);
// 保存得到的method,映射关系为 属性名 -> List<Method> ,避免同一个name获得多个方法
addMethodConflict(conflictingSetters, name, method);
}
}
}
// 为每个属性选定唯一一个Setter方法(方法详情略)
// 规则如下:
// 遍历属性名称的所有方法
// 1. 若通过属性名称查询Getter方法的返回类型和当前方法的入参类型一致,选定该方法作为Setter方法
// 2. 若通过属性名称查询Getter方法的返回类型和当前方法的入参类型不一致,选取入参类型是最子类的方法
resolveSetterConflicts(conflictingSetters);
}
// ...
// 添加当前类属性的访问和赋值方法
private void addFields(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (canAccessPrivateMethods()) {
try {
field.setAccessible(true);
} catch (Exception e) {
}
}
if (field.isAccessible()) {
if (!setMethods.containsKey(field.getName())) {
int modifiers = field.getModifiers();
// 属性的set方法不能同时被static和final修饰
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
// 在全局属性setMethods集合中,添加为该属性赋值的SetFieldInvoker对象
addSetField(field);
}
}
// 在全局属性getMethods集合中,添加为该属性赋值的GetFieldInvoker对象
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
}
// 进一步解析父类的属性
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
// ...
/*
* TODO 这个方法在类中多次使用,为什么不采取一个集合保存解析得到的Method[]??????
*
* 这个方法会返回当前类,当前类继承的父类,当前类实现的接口中所有的方法,
* 之所以不简单的采用Class.getMethods(),是因为也想获取到private访问权限的方法
*/
private Method[] getClassMethods(Class<?> cls) {
Map<String, Method> uniqueMethods = new HashMap<String, Method>();
Class<?> currentClass = cls;
while (currentClass != null && currentClass != Object.class) {
// 根据一定规则将方法的明明标记作为key,淘汰所有JVM自己生成的桥接方法(桥接方法用于处理泛型方法)
// 如果存在方法明明标记一致的情况,考虑存在重载的方法,以最后一个方法为准
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
// 同时也去获取接口的方法,因为当前cls入参可能是个抽象类
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
addUniqueMethods(uniqueMethods, anInterface.getMethods());
}
// 继续获取父类的方法
currentClass = currentClass.getSuperclass();
}
Collection<Method> methods = uniqueMethods.values();
return methods.toArray(new Method[methods.size()]);
}
// ...
}