您好,登錄后才能下訂單哦!
mybatis源碼如何分析反射模塊,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
mybatis的反射模塊,對(duì)應(yīng)reflection包。反射模塊作為myabits中的基礎(chǔ)支持層,對(duì)java原生的反射進(jìn)行了良好的封裝,提供了簡潔易用的api,方便上層調(diào)用,并對(duì)反射操作做了一系列優(yōu)化,例如緩存了類的元數(shù)據(jù),提高了反射操作的性能。
今天重點(diǎn)看下Reflector類
org.apache.ibatis.reflection.Reflector,反射器,每個(gè)Reflector對(duì)應(yīng)一個(gè)類。Reflector會(huì)緩存反射操作需要的類的信息,例如:構(gòu)造方法、屬性名、setting/getting方法等等。
public class Reflector { /** * 對(duì)應(yīng)的類 */ private final Class<?> type; /** * 可讀屬性數(shù)組 */ private final String[] readablePropertyNames; /** * 可寫屬性數(shù)組 */ private final String[] writablePropertyNames; /** * 屬性對(duì)應(yīng)的setting方法的映射 * key:屬性名稱 * value:Invoker對(duì)象 */ private final Map<String, Invoker> setMethods = new HashMap<>(); /** * 屬性對(duì)應(yīng)的getting方法的映射 * key:屬性名稱 * value:Invoker對(duì)象 */ private final Map<String, Invoker> getMethods = new HashMap<>(); /** * 屬性對(duì)應(yīng)的setting方法的方法參數(shù)類型的映射 * key:屬性名稱 * value:方法參數(shù)類型 */ private final Map<String, Class<?>> setTypes = new HashMap<>(); /** * 屬性對(duì)應(yīng)的getting方法的方法參數(shù)類型的映射 * key:屬性名稱 * value:方法參數(shù)類型 */ private final Map<String, Class<?>> getTypes = new HashMap<>(); /** * 默認(rèn)構(gòu)造方法 */ private Constructor<?> defaultConstructor; /** * 不區(qū)分大小寫的屬性集合 */ private Map<String, String> caseInsensitivePropertyMap = new HashMap<>(); public Reflector(Class<?> clazz) { // 設(shè)置對(duì)應(yīng)的類 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(Class<?> cls),代碼如下:
private void addGetMethods(Class<?> cls) { // <1> 屬性與其 getting 方法的映射 Map<String, List<Method>> conflictingGetters = new HashMap<>(); //獲取所有方法 Method[] methods = getClassMethods(cls); for (Method method : methods) { //參數(shù)大于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的沖突方法(一個(gè)屬性,只保留一個(gè)對(duì)應(yīng)的方法) resolveGetterConflicts(conflictingGetters); }
<1>處:Map<String, List<Method>> conflictingGetters:變量,屬性與其getting方法的映射。因?yàn)楦割惡妥宇惗伎赡芏x了相同屬性的getting方法,所以value會(huì)是個(gè)數(shù)組。
<2>處:addMethodConflict方法用到了Map的computeIfAbsent方法,這個(gè)方法僅jdk1.8即以上支持。這個(gè)方法很值得推薦。很慚愧,雖然1.8用了很久了,但是jdk提供的很多很便捷的方法,仍然沒有使用,此處算是學(xué)到了。
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) { List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>()); list.add(method); }
接下來,重點(diǎn)看resolveGetterConflicts方法
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) { // 遍歷每個(gè)屬性,查找其最匹配的方法。因?yàn)樽宇惪梢愿矊懜割惖姆椒?,所以一個(gè)屬性,可能對(duì)應(yīng)多個(gè) 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)) { // 返回值類型相同,應(yīng)該在 getClassMethods 方法中,已經(jīng)合并。所以拋出 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> 符合選擇子類。因?yàn)樽宇惪梢孕薷姆糯蠓祷刂?。例如,父類的一個(gè)方法的返回值為 List ,子類對(duì)該方法的返回值可以覆寫為 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> 處,基于返回類型比較。重點(diǎn)在 <1.1> 和 <1.2> 的情況,因?yàn)樽宇惪梢孕薷姆糯蠓祷刂?,所以在出現(xiàn)這個(gè)情況時(shí),選擇子類的該方法。例如,父類的一個(gè)方法的返回值為 List ,子類對(duì)該方法的返回值可以覆寫為 ArrayList 。
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。