mybatis原始碼分析---反射模組
阿新 • • 發佈:2019-07-05
本文參考(精盡 MyBatis 原始碼分析 —— 反射模組)[http://svip.iocoder.cn/MyBatis/reflection-package/#] 存粹作為一個記錄,加深自己的理解。
mybatis的反射模組,對應reflection包。反射模組作為myabits中的基礎支援層,對java原生的反射進行了良好的封裝,提供了簡潔易用的api,方便上層呼叫,並對反射操作做了一系列優化,例如快取了類的元資料,提高了反射操作的效能。
今天重點看下Reflector類
org.apache.ibatis.reflection.Reflector,反射器,每個Reflector對應一個類。Reflector會快取反射操作需要的類的資訊,例如:構造方法、屬性名、setting/getting方法等等。
public class Reflector { /** * 對應的類 */ private final Class<?> type; /** * 可讀屬性陣列 */ private final String[] readablePropertyNames; /** * 可寫屬性陣列 */ private final String[] writablePropertyNames; /** * 屬性對應的setting方法的對映 * key:屬性名稱 * value:Invoker物件 */ private final Map<String, Invoker> setMethods = new HashMap<>(); /** * 屬性對應的getting方法的對映 * key:屬性名稱 * value:Invoker物件 */ private final Map<String, Invoker> getMethods = new HashMap<>(); /** * 屬性對應的setting方法的方法引數型別的對映 * key:屬性名稱 * value:方法引數型別 */ private final Map<String, Class<?>> setTypes = new HashMap<>(); /** * 屬性對應的getting方法的方法引數型別的對映 * key:屬性名稱 * value:方法引數型別 */ private final Map<String, Class<?>> getTypes = new HashMap<>(); /** * 預設構造方法 */ private Constructor<?> defaultConstructor; /** * 不區分大小寫的屬性集合 */ private Map<String, String> caseInsensitivePropertyMap = new HashMap<>(); public Reflector(Class<?> clazz) { // 設定對應的類 type = clazz; // <1> 初始化 defaultConstructor addDefaultConstructor(clazz); // <2> // 初始化 getMethods 和 getTypes ,通過遍歷 getting 方法 addGetMethods(clazz); // <3> // 初始化 setMethods 和 setTypes ,通過遍歷 setting 方法。 addSetMethods(clazz); // <4> // 初始化 getMethods + getTypes 和 setMethods + setTypes ,通過遍歷 fields 屬性。 addFields(clazz); // <5> 初始化 readablePropertyNames、writeablePropertyNames、caseInsensitivePropertyMap 屬性 readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writeablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } }
addGetMethods
addGetMethods(Class<?> cls),程式碼如下:
private void addGetMethods(Class<?> cls) { // <1> 屬性與其 getting 方法的對映 Map<String, List<Method>> conflictingGetters = new HashMap<>(); //獲取所有方法 Method[] methods = getClassMethods(cls); for (Method method : methods) { //引數大於0,說明不是getting方法,忽略 if (method.getParameterTypes().length > 0) { continue; } //以get或者is開頭,說明是getting方法 String name = method.getName(); if ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2)) { //獲取屬性 name = PropertyNamer.methodToProperty(name); //<2> 新增到conflictingGetters addMethodConflict(conflictingGetters, name, method); } } //解決getting的衝突方法(一個屬性,只保留一個對應的方法) resolveGetterConflicts(conflictingGetters); }
- <1>處:Map<String, List<Method>> conflictingGetters:變數,屬性與其getting方法的對映。因為父類和子類都可能定義了相同屬性的getting方法,所以value會是個陣列。
- <2>處:addMethodConflict方法用到了Map的computeIfAbsent方法,這個方法僅jdk1.8即以上支援。這個方法很值得推薦。很慚愧,雖然1.8用了很久了,但是jdk提供的很多很便捷的方法,仍然沒有使用,此處算是學到了。
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
list.add(method);
}
接下來,重點看resolveGetterConflicts方法
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
// 遍歷每個屬性,查詢其最匹配的方法。因為子類可以覆寫父類的方法,所以一個屬性,可能對應多個 getting 方法
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null; // 最匹配的方法
String propName = entry.getKey();
for (Method candidate : entry.getValue()) {
// winner 為空,說明 candidate 為最匹配的方法
if (winner == null) {
winner = candidate;
continue;
}
// <1> 基於返回型別比較
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
// 型別相同
if (candidateType.equals(winnerType)) {
// 返回值型別相同,應該在 getClassMethods 方法中,已經合併。所以丟擲 ReflectionException 異常
if (!boolean.class.equals(candidateType)) {
throw new ReflectionException(
"Illegal overloaded getter method with ambiguous type for property "
+ propName + " in class " + winner.getDeclaringClass()
+ ". This breaks the JavaBeans specification and can cause unpredictable results.");
// 選擇 boolean 型別的 is 方法
} else if (candidate.getName().startsWith("is")) {
winner = candidate;
}
// 不符合選擇子類
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
// <1.1> 符合選擇子類。因為子類可以修改放大返回值。例如,父類的一個方法的返回值為 List ,子類對該方法的返回值可以覆寫為 ArrayList 。
} else if (winnerType.isAssignableFrom(candidateType)) {
winner = candidate;
// <1.2> 返回型別衝突,丟擲 ReflectionException 異常
} else {
throw new ReflectionException(
"Illegal overloaded getter method with ambiguous type for property "
+ propName + " in class " + winner.getDeclaringClass()
+ ". This breaks the JavaBeans specification and can cause unpredictable results.");
}
}
// <2> 新增到 getMethods 和 getTypes 中
addGetMethod(propName, winner);
}
}
- <1> 處,基於返回型別比較。重點在 <1.1> 和 <1.2> 的情況,因為子類可以修改放大返回值,所以在出現這個情況時,選擇子類的該方法。例如,父類的一個方法的返回值為 List ,子類對該方法的返回值可以覆寫為