Mybatis映射體系
Mybatis映射體系
MetaObject
MetaObject應(yīng)用
查找屬性:
忽略大小寫,支持駝峰训唱,支持子屬性獲取屬性值:
基于點(diǎn)獲取子屬性,如user.name
基于索引獲取屬性列表值,如users[1].id
基于key獲取map值至壤,如user[name]設(shè)置屬性
可設(shè)置子屬性值
持自動(dòng)創(chuàng)建子屬性對(duì)象(必須有無參構(gòu)造器,且不能是集合)
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對(duì)象
MetaObject metaobject = configuration.newMetaObject(metaObjectTest);
// 通過MetaObject設(shè)置對(duì)象屬性值
metaObjectTest.setName("oldName");
System.out.println(metaObjectTest.getName());
metaobject.setValue("name", "newName");
// 通過輸出結(jié)果枢纠,可以看到orderNo被MetaObject設(shè)置的值覆蓋
System.out.println(metaObjectTest.getName());
System.out.println("--->");
// 通過MetaObject獲取對(duì)象屬性值
System.out.println("通過MetaObject獲取對(duì)象屬性值:" + metaobject.getValue("name"));
System.out.println("--->");
// 通過MetaObject設(shè)置嵌套屬性的值像街,它會(huì)創(chuàng)建一個(gè)空的嵌套對(duì)象,并為其屬性賦值
metaobject.setValue("address.addressDetail", "詳細(xì)地址");
System.out.println("通過MetaObject自動(dòng)創(chuàng)建內(nèi)嵌對(duì)象晋渺,賦值之后獲取 : "
+ metaobject.getValue("address.addressDetail"));
System.out.println("--->");
System.out.println("通過駝峰命名法獲取屬性名稱 :"
+ metaobject.findProperty("address.address_detail", true));
System.out.println("--->");
// MetaObject無法自動(dòng)創(chuàng)建List類型的內(nèi)嵌對(duì)象镰绎,需要手動(dòng)設(shè)置,如:
Address address = new Address();
address.setProvince("福建省");
List<Address> addressList = new ArrayList<>();
addressList.add(address);
metaobject.setValue("addressList", addressList);
metaobject.setValue("addressList[0]", address);
// 訪問集合對(duì)象成員的方式(也可以訪問成員的屬性值)
System.out.println("通過MetaObject獲取List類型成員屬性 : "
+ metaobject.getValue("addressList[0].province"));
System.out.println("--->");
// MetaObject同樣無法自動(dòng)創(chuàng)建Map類型的內(nèi)嵌對(duì)象木西,需要手動(dòng)設(shè)置跟狱,如:
metaobject.setValue("additionParams", new HashMap<>());
// 設(shè)置map屬性的鍵值對(duì),以及訪問鍵值
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);
// 存在子屬性u(píng)ser.name
if (prop.hasNext()) {
// 如果存在子屬性户魏,則通過IndexedName再次創(chuàng)建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屬性時(shí)叼丑,不存在子屬性关翎,通過反射獲取屬性值(可參考下面BeanWrapper類源碼)
else {
return objectWrapper.get(prop);
}
}
// 以解析comments[0].user.name為例
public MetaObject metaObjectForProperty(String name) {
// 這里會(huì)先后獲取comments[0]、user兩個(gè)屬性的值
Object value = getValue(name);
// 將上面通過某個(gè)屬性獲取到的對(duì)象鸠信,繼續(xù)將這些對(duì)象解析為MetaObject對(duì)象
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
PropertyTokenizer
屬性名稱分詞器纵寝,它首先會(huì)根據(jù)"."符號(hào),將屬性名稱拆分星立,比如comments[0].user.name通過多次的屬性名稱分詞爽茴,會(huì)被劃分為comments[0]、user绰垂、name三部分室奏,再根據(jù)這三部分創(chuàng)建各自的MetaObject元對(duì)象,進(jìn)一步解析子屬性劲装,源碼如下:
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
private String name;
private final String indexedName;
// index之所以為String類型胧沫,是因?yàn)槌隽薈ollection是數(shù)值類型,還有Map類型采用String類型的key
private String index;
private final String children;
// 假定入?yún)ullname屬性全名為:comments[0].user.name
// 最終的解析結(jié)果為:
// name = comments
// indexedName = comments[0]
// index = 0
// children = user.name
public PropertyTokenizer(String fullname) {
// 首先占业,獲取第一個(gè).的偏移量
int delim = fullname.indexOf('.');
// 存在.的情況
if (delim > -1) {
// 先將name賦值為第一個(gè).號(hào)之前的全部字符绒怨,這里為comments[0]
name = fullname.substring(0, delim);
// childre賦值為第一個(gè).之后的全部字符串,這里為user.name
children = fullname.substring(delim + 1);
} else {
// 不存在.的字符串谦疾,直接將name賦值為整個(gè)入?yún)ullname
name = fullname;
children = null;
}
// 將comments[0]賦值給indexedName
indexedName = name;
// 判斷name是否存在[符號(hào)南蹂,存在的話將name賦值為[]符號(hào)外的字符串,這里將name重新賦值為comments
delim = name.indexOf('[');
if (delim > -1) {
// index賦值為[]內(nèi)的下標(biāo)數(shù)值念恍,如果不存在[]六剥,index為默認(rèn)值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
封裝目標(biāo)對(duì)象和目標(biāo)對(duì)象類元數(shù)據(jù)晚顷,可以通過它反射獲取屬性值,這個(gè)類只能獲取頂層屬性的值仗考,比如通過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) {
// 通過屬性分詞器判斷該屬性是否是一個(gè)集合秃嗜,如果屬性分詞器記錄的index屬性為null权均,表示當(dāng)前要解析的屬性不是集合
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獲取屬性值對(duì)應(yīng)的MethodInvoker,內(nèi)部封裝了JDK反射的method對(duì)象(詳見下方源碼)
// 注意這里是通過屬性分詞器的name屬性獲取屬性值
// 當(dāng)嘗試通過BeanWrapper獲取屬性值锅锨,且傳入的屬性為user.name時(shí)叽赊,這里的prop.getName()將會(huì)獲取到user
//(參看上面PropertyTokenizer如何解析屬性名稱)
// 因此其實(shí)獲取到的時(shí)user屬性值,無法獲取user對(duì)象的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必搞,通過它類反射獲取屬性值或者設(shè)置屬性值
public class MethodInvoker implements Invoker {
private final Class<?> type;
private final Method method;
public MethodInvoker(Method method) {
this.method = method;
// 兼容Getter和Setter方法必指,Setter根據(jù)入?yún)㈩愋蜎Q定,Getter根據(jù)出參類型決定
if (method.getParameterTypes().length == 1) {
type = method.getParameterTypes()[0];
} else {
type = method.getReturnType();
}
}
// 反射調(diào)用Getter或者Setter方法
public Object invoke(Object target, Object[] args)
throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, args);
}
// 返回當(dāng)前操作的屬性類型
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封裝的目標(biāo)對(duì)象Object
if ("".equals(prop.getName())) {
return object;
} else {
// 若解析的屬性是comments[0],將會(huì)返回comments屬性名對(duì)應(yīng)的對(duì)象值
return metaObject.getValue(prop.getName());
}
}
// 從集合中獲取某個(gè)成員
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
// 若屬性值為Map類型恕洲,PropertyTokenizer解析得到的index為key名稱
if (collection instanceof Map) {
return ((Map) collection).get(prop.getIndex());
}
// 從List塔橡、數(shù)組類型取值
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 {
// ...
}
}
}
// 為集合屬性設(shè)置值
protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
// Map類型,以key為index值霜第,value為入?yún)alue設(shè)置一個(gè)Map.Entry
if (collection instanceof Map) {
((Map) collection).put(prop.getIndex(), value);
}
// 為L(zhǎng)ist葛家、數(shù)組類型賦值
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
可以解析一個(gè)類的所有屬性,記錄所有的Setter和Getter方法
public class Reflector {
// 解析的類
private final Class<?> type;
private final String[] readablePropertyNames;
private final String[] writeablePropertyNames;
// 通過類的Method解析
// 封裝所有g(shù)et開頭的方法泌类,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開頭呕屎,被認(rèn)定為Setter方法的屬性類型,以及所有Field的類型
// 如果是List<T>返回泛型類型敬察,如果是T[]同樣返回泛型
private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
// 封裝所有is秀睛、get開頭,被認(rèn)定為Getter方法的屬性類型莲祸,以及所有Filed的類型
// 如果是List<T>返回泛型類型蹂安,如果是T[]同樣返回泛型
private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
// 解析得到的無參構(gòu)造器
private Constructor<?> defaultConstructor;
// 保存所有可以取值椭迎、設(shè)置的屬性名稱的并集,key為屬性名稱全大寫字符串田盈,value為屬性名稱
private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();
public Reflector(Class<?> clazz) {
type = clazz;
// 獲取無參構(gòu)造器
addDefaultConstructor(clazz);
// 添加所有合法的Getter
addGetMethods(clazz);
// 添加所有合法的Setter
addSetMethods(clazz);
// 通過類的Field添加另外一批Getter和Setter方法
addFields(clazz);
// 使用數(shù)組保存上述通過Method和Filed解析得到的可以獲取值的屬性名稱
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
// 使用數(shù)組保存上述通過Method和Filed解析得到的可以設(shè)置值的屬性名稱
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
// 將所有可以賦值畜号、可以取值的屬性名稱,轉(zhuǎn)換為大寫字符串保存到一個(gè)Map中
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
// 獲取默認(rèn)無參構(gòu)造器
private void addDefaultConstructor(Class<?> clazz) {
// 獲取當(dāng)前類型定義的全部構(gòu)造器
Constructor<?>[] consts = clazz.getDeclaredConstructors();
// 遍歷所有構(gòu)造器允瞧,找到入?yún)?的構(gòu)造器简软,并將其設(shè)置為可訪問的
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) {
// 保存解析得到同名屬性,卻有多個(gè)方法的沖突Map
Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
// 獲取cls(包括父類述暂、接口定義的)上所有的方法
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
// Getter方法應(yīng)該沒有入?yún)⒈陨挥涗浻袇?shù)的方法
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艺配,并將剩下的第一個(gè)字符變?yōu)樾? // 如:getUsername => username
name = PropertyNamer.methodToProperty(name);
// 保存得到的method察郁,映射關(guān)系為 屬性名 -> List<Method> ,避免同一個(gè)name獲得多個(gè)方法
addMethodConflict(conflictingGetters, name, method);
}
}
// 為每個(gè)屬性選定唯一一個(gè)Getter方法(方法詳情略)
// 規(guī)則如下:
// 遍歷每個(gè)方法的返回類型转唉,
// 1. 存在返回類型相同的多個(gè)方法
// 1.1 如果不是boolean類型屬性皮钠,存在多個(gè)方法返回類型一致,拋出異常
// 1.2 如果是boolean類型屬性酝掩,以最后一個(gè)is開頭的方法名稱作為該屬性的Getter方法
// 2. 如果多個(gè)Getter方法返回類型不一致鳞芙,以最小的子類作為Getter方法
// 3. 解析得到的屬性名稱不能是$開頭、serialVersionUID期虾、class這三種形式的名稱原朝,不添加Getter方法
// 4. 將得到的Getter方法,封裝為MethodInvoker對(duì)象
resolveGetterConflicts(conflictingGetters);
}
// ...
private void addSetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
// 獲取cls(包括父類镶苞、接口定義的)上所有的方法
Method[] methods = getClassMethods(cls);
// 遍歷所有以set開頭喳坠,并且set之后還有字符的方法名稱,且方法只有一個(gè)入?yún)? for (Method method : methods) {
String name = method.getName();
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
// 刪除方法名稱前綴get茂蚓、is壕鹉、set,并將剩下的第一個(gè)字符變?yōu)樾? // 如:getUsername => username
name = PropertyNamer.methodToProperty(name);
// 保存得到的method聋涨,映射關(guān)系為 屬性名 -> List<Method> 晾浴,避免同一個(gè)name獲得多個(gè)方法
addMethodConflict(conflictingSetters, name, method);
}
}
}
// 為每個(gè)屬性選定唯一一個(gè)Setter方法(方法詳情略)
// 規(guī)則如下:
// 遍歷屬性名稱的所有方法
// 1. 若通過屬性名稱查詢Getter方法的返回類型和當(dāng)前方法的入?yún)㈩愋鸵恢拢x定該方法作為Setter方法
// 2. 若通過屬性名稱查詢Getter方法的返回類型和當(dāng)前方法的入?yún)㈩愋筒灰恢码拱祝x取入?yún)㈩愋褪亲钭宇惖姆椒? resolveSetterConflicts(conflictingSetters);
}
// ...
// 添加當(dāng)前類屬性的訪問和賦值方法
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方法不能同時(shí)被static和final修飾
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
// 在全局屬性setMethods集合中脊凰,添加為該屬性賦值的SetFieldInvoker對(duì)象
addSetField(field);
}
}
// 在全局屬性getMethods集合中,添加為該屬性賦值的GetFieldInvoker對(duì)象
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
}
// 進(jìn)一步解析父類的屬性
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
// ...
/*
* TODO 這個(gè)方法在類中多次使用茂腥,為什么不采取一個(gè)集合保存解析得到的Method[]狸涌?切省??帕胆?朝捆??
*
* 這個(gè)方法會(huì)返回當(dāng)前類懒豹,當(dāng)前類繼承的父類芙盘,當(dāng)前類實(shí)現(xiàn)的接口中所有的方法,
* 之所以不簡(jiǎn)單的采用Class.getMethods()歼捐,是因?yàn)橐蚕氆@取到private訪問權(quán)限的方法
*/
private Method[] getClassMethods(Class<?> cls) {
Map<String, Method> uniqueMethods = new HashMap<String, Method>();
Class<?> currentClass = cls;
while (currentClass != null && currentClass != Object.class) {
// 根據(jù)一定規(guī)則將方法的明明標(biāo)記作為key何陆,淘汰所有JVM自己生成的橋接方法(橋接方法用于處理泛型方法)
// 如果存在方法明明標(biāo)記一致的情況,考慮存在重載的方法豹储,以最后一個(gè)方法為準(zhǔn)
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
// 同時(shí)也去獲取接口的方法贷盲,因?yàn)楫?dāng)前cls入?yún)⒖赡苁莻€(gè)抽象類
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
addUniqueMethods(uniqueMethods, anInterface.getMethods());
}
// 繼續(xù)獲取父類的方法
currentClass = currentClass.getSuperclass();
}
Collection<Method> methods = uniqueMethods.values();
return methods.toArray(new Method[methods.size()]);
}
// ...
}