mybatis源碼分析---反射模塊

本文參考(精盡 MyBatis 源碼分析 —— 反射模塊)[http://svip.iocoder.cn/MyBatis/reflection-package/#] 存粹做爲一個記錄,加深本身的理解。java

mybatis的反射模塊,對應reflection包。反射模塊做爲myabits中的基礎支持層,對java原生的反射進行了良好的封裝,提供了簡潔易用的api,方便上層調用,並對反射操做作了一系列優化,例如緩存了類的元數據,提升了反射操做的性能。apache

今天重點看下Reflectorapi

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方法mybatis

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 ,子類對該方法的返回值能夠覆寫爲 ArrayList 。
相關文章
相關標籤/搜索