[MyBatis源碼分析 - 反射器模塊 - 組件一] Reflector

一、簡(jiǎn)介

??Reflector是整個(gè)反射器模塊的基礎(chǔ)嗜历,每個(gè)Reflector對(duì)象都對(duì)應(yīng)一個(gè)類赊时,在其構(gòu)造函數(shù)中吨铸,根據(jù)傳入的Class對(duì)象,調(diào)用Java的反射API蛋叼,獲取并緩存反射操作需要用到的這個(gè)類的元信息焊傅。


二、成員屬性

  private Class<?> type;      // 對(duì)應(yīng)的Class類型
  private String[] readablePropertyNames = EMPTY_STRING_ARRAY;  // 可讀屬性的名稱集合狈涮,可讀屬性就是存在相應(yīng)getter方法的屬性狐胎,初始值為空數(shù)組
  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY; // 可寫(xiě)屬性的名稱集合,可寫(xiě)屬性就是存在相應(yīng)setter方法的屬性歌馍,初始值為空數(shù)組
  private Map<String, Invoker> setMethods = new HashMap<String, Invoker>();   // 記錄了屬性相應(yīng)的setter方法握巢,key是屬性名稱,value是Invoker對(duì)象松却,其封裝了setter方法對(duì)應(yīng)的Method對(duì)象
  private Map<String, Invoker> getMethods = new HashMap<String, Invoker>();   // 屬性相應(yīng)的getter方法集合暴浦,key是屬性名稱溅话,value是Invoker對(duì)象
  private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();    // 記錄了屬性相應(yīng)的setter方法的參數(shù)值類型,key是屬性名稱歌焦,value是setter方法的參數(shù)類型
  private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();    // 記錄了屬性相應(yīng)的getter方法的返回值類型飞几,key是屬性名稱,value是getter方法的返回值類型
  private Constructor<?> defaultConstructor;                    // 記錄了默認(rèn)的構(gòu)造方法

  private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>(); // 記錄了所有屬性名稱的集合独撇,key統(tǒng)一記錄成大寫(xiě)
  • type:傳入的需要解析元信息的類屑墨,每個(gè) Reflector 對(duì)象都對(duì)應(yīng)一個(gè)類。
  • readablePropertyNames:可讀屬性的名稱集合纷铣,一般Java Bean中屬性定義為私有卵史,要讀取屬性值,必須通過(guò) getter 方法來(lái)獲取搜立,換句話說(shuō)以躯,假如屬性有相應(yīng)的 getter 方法,我們就認(rèn)定為該屬性為可讀屬性啄踊。假如屬性名為 username 忧设,則方法名一般為 getUsername() ,假如屬性是布爾值如 open 社痛,則方法名一般為 isOpen() 见转。
  • writeablePropertyNames:可寫(xiě)屬性的名稱集合,同樣的類似上面的蒜哀,如果屬性有相應(yīng)的 setter 方法,可認(rèn)定為可寫(xiě)屬性吏砂。
  • setMethods:實(shí)際運(yùn)行中如將SQL執(zhí)行返回結(jié)果集映射為Java對(duì)象撵儿,設(shè)置對(duì)象屬性的操作需要反射來(lái)完成,這些操作封裝在 Invoker 對(duì)象中狐血,該對(duì)象緩存屬性對(duì)應(yīng)的 Field 對(duì)象淀歇,設(shè)置屬性值依賴該 Field 對(duì)象完成;key對(duì)應(yīng)屬性名稱匈织,value對(duì)應(yīng) Invoker 對(duì)象浪默。
  • getMethods:同樣獲取對(duì)象屬性值需要反射來(lái)完成,原理跟上面類似缀匕,不過(guò)反射的操作由 set 變成 get 纳决。
  • setTypes:不管是執(zhí)行SQL前參數(shù)的預(yù)編譯,還是執(zhí)行完之后將結(jié)果集映射為Java對(duì)象乡小,都需要知道屬性的類型阔加,底層的JDBC對(duì)象才知道要調(diào)用哪些相應(yīng)的方法;本屬性緩存了屬性名和屬性的 setter 方法的映射關(guān)系满钟。
  • getTypes:同上胜榔,本屬性緩存了屬性名和屬性的 getter 方法的映射關(guān)系胳喷。
  • defaultConstructor:傳入解析的類的默認(rèn)構(gòu)造方法,常用于通過(guò)反射創(chuàng)建Java對(duì)象夭织。
  • caseInsensitivePropertyMap:記錄了所有屬性名稱的集合吭露,key統(tǒng)一處理成大寫(xiě),value則為屬性名尊惰。


三奴饮、構(gòu)造方法

??Reflector的構(gòu)造方法中,會(huì)解析執(zhí)行的Class對(duì)象择浊,并填充上述集合戴卜,源碼如下:

  public Reflector(Class<?> clazz) {
    type = clazz;                   // 初始化type字段
    addDefaultConstructor(clazz);   // 查找clazz的默認(rèn)構(gòu)造方法(無(wú)參構(gòu)造方法),通過(guò)反射獲取所有構(gòu)造方法琢岩,通過(guò)遍歷找出參數(shù)長(zhǎng)度為0的構(gòu)造方法
    addGetMethods(clazz);           // 獲取clazz中的getter方法投剥,并填充getMethods集合和getTypes集合
    addSetMethods(clazz);           // 獲取clazz中的setter方法,并填充setMethods集合和setTypes集合
    addFields(clazz);               // 處理沒(méi)有g(shù)etter/setter的字段

    // 根據(jù)getMethods/setMethods集合担孔,初始化可讀/寫(xiě)屬性的名稱集合
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);

    // 初始化caseInsensitivePropertyMap集合江锨,其中記錄了所有大寫(xiě)格式的屬性名稱
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

??首先初始化 type 屬性,然后在 addDefaultConstructor() 方法中根據(jù)傳入的Class對(duì)象的默認(rèn)構(gòu)造方法初始化 defaultConstructor 屬性糕篇,接著調(diào)用 addGetMethods() 方法填充 getMethodsgetTypes 集合啄育,同理調(diào)用 addSetMethods() 方法填充 setMethodssetTypes 集合,調(diào)用 addFields() 方法去處理沒(méi)有g(shù)etter/setter的其余字段拌消,因?yàn)榈讓颖举|(zhì)是通過(guò)反射來(lái)獲取和設(shè)置屬性值挑豌,該方法也會(huì)填充上述的四個(gè)集合,最后再將收集到的屬性名根據(jù)允許獲取和設(shè)置值來(lái)填充 readablePropertyNameswriteablePropertyNames 集合墩崩,再將這兩個(gè)集合中的所有屬性名的大寫(xiě)作為key來(lái)填充 caseInsensitivePropertyMap氓英。


四、方法功能

1鹦筹、addDefaultConstructor(Class<?> clazz)

【功能】獲取類的默認(rèn)構(gòu)造方法铝阐。
【源碼與注釋】

  private void addDefaultConstructor(Class<?> clazz) {
    // 獲取類的所有構(gòu)造方法
    Constructor<?>[] consts = clazz.getDeclaredConstructors();
    // 遍歷所有構(gòu)造方法
    for (Constructor<?> constructor : consts) {
      // 找到無(wú)參構(gòu)造方法,該方法即為默認(rèn)構(gòu)造方法
      if (constructor.getParameterTypes().length == 0) {
        // 構(gòu)造方法有可能被private修飾铐拐,需要設(shè)置其可以訪問(wèn)
        if (canAccessPrivateMethods()) {
          try {
            constructor.setAccessible(true);
          } catch (Exception e) {
            // Ignored. This is only a final precaution, nothing we can do.
          }
        }
        // 如果無(wú)參構(gòu)造方法有訪問(wèn)的權(quán)限徘键,則賦值給defaultConstructor
        if (constructor.isAccessible()) {
          this.defaultConstructor = constructor;
        }
      }
    }
  }


2、canAccessPrivateMethods()

【功能】判斷系統(tǒng)安全管理器是否允許修改類的可訪問(wèn)性遍蟋。
【源碼與注釋】

  // 判斷是否可以修改可訪問(wèn)性
  private static boolean canAccessPrivateMethods() {
    try {
      // 獲取系統(tǒng)安全管理器
      SecurityManager securityManager = System.getSecurityManager();
      // 如果設(shè)置了安全管理器吹害,檢查是否有通過(guò)反射來(lái)訪問(wèn)protected、private的成員和方法的權(quán)限
      if (null != securityManager) {
        securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
      }
    } catch (SecurityException e) {
      // 如果沒(méi)有權(quán)限匿值,則權(quán)限檢查會(huì)拋出異常赠制,返回false表示不允許訪問(wèn)私有方法
      return false;
    }
    // 如果未設(shè)置安全管理器,或者有指定權(quán)限,則返回true表示允許訪問(wèn)私有方法
    return true;
  }


3钟些、addGetMethods()

【功能】獲取類的 getter 方法并填充 getMethodsgetTypes 集合烟号。
【源碼與注釋】

  private void addGetMethods(Class<?> cls) {
    // (1)定義屬性名和對(duì)應(yīng)getter方法的映射
    Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
    // (2)獲取當(dāng)前類及其父類中定義的所有方法
    Method[] methods = getClassMethods(cls);
    // (3)遍歷所有方法,篩選出 getter 方法并添加到 conflictingGetters 中
    for (Method method : methods) {
      String name = method.getName();
      // 篩選出getter方法: 一般屬性名為aaBb對(duì)應(yīng)的getter方法為getAaBb()
      //                  如果屬性是布爾值政恍,則對(duì)應(yīng)的getter方法為isAaBb()
      // 如果子類重寫(xiě)覆蓋了父類getter方法導(dǎo)致方法簽名不一致
      // eg:
      //   父類定義:  List<String> getList()      方法簽名為: java.util.List<String>#getList
      //   子類定義: ArrayList<String> getList() 方法簽名為: java.util.ArrayList<String>#getList
      // 我們要取的肯定是返回參數(shù)類型更加具體的方法汪拥,但是方法簽名不一致導(dǎo)致沖突,就先將屬性名和對(duì)應(yīng)的沖突方法緩存到conflictingGetters
      // 再交給resolveGetterConflicts()方法進(jìn)一步處理
      // (3.1)以 get 和 is 開(kāi)頭篙耗,并且方法名長(zhǎng)度分別長(zhǎng)于 3 和 2 才表明這是個(gè) getter 方法
      if (name.startsWith("get") && name.length() > 3) {
        // (3.2)只有方法參數(shù)個(gè)數(shù)為0的方法才是 getter 方法
        if (method.getParameterTypes().length == 0) {
          // (3.3)根據(jù)方法名獲取屬性名
          name = PropertyNamer.methodToProperty(name);
          // (3.4)屬性名和方法的映射添加到 conflictingGetters 中
          addMethodConflict(conflictingGetters, name, method);
        }
      } else if (name.startsWith("is") && name.length() > 2) {
        if (method.getParameterTypes().length == 0) {
          name = PropertyNamer.methodToProperty(name);
          addMethodConflict(conflictingGetters, name, method);
        }
      }
    }
    // (4)解決簽名不同但是方法重復(fù)的 getter 方法迫筑,一般屬性的 getter 只需要有一個(gè)即可
    resolveGetterConflicts(conflictingGetters);
  }

【解析】
(1)定義屬性名和 getter 方法的映射,因?yàn)樽宇惪赡苤貙?xiě)父類方法宗弯,比如返回值類型更加具體脯燃,所以映射的值是一個(gè)列表。
(2)調(diào)用 #getClassMethods() 方法來(lái)獲取類的所有方法蒙保,包括實(shí)現(xiàn)接口定義的方法辕棚、繼承自父類的方法,并沿著繼承鏈向上追溯邓厕,具體實(shí)現(xiàn)可閱讀下面關(guān)于該方法的詳解逝嚎。
(3)遍歷所有方法,篩選出 getter 方法

  • (3.1)只有形如 getXxx()isYyy() 的方法才是 getter 方法
  • (3.2)getter 方法的參數(shù)個(gè)數(shù)應(yīng)該為0
  • (3.3)根據(jù)方法名獲得屬性名
  • (3.4)屬性名和方法的映射添加到 conflictingGetters 中详恼,如果屬性名第一次添加方法补君,則創(chuàng)建List,否則添加到已經(jīng)創(chuàng)建的List中昧互,源碼如下:
  private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    // 獲取List
    List<Method> list = conflictingMethods.get(name);
    // List為空挽铁,說(shuō)明該屬性是第一次,先創(chuàng)建List硅堆,再將List添加到conflictingMethods中
    if (list == null) {
      list = new ArrayList<Method>();
      conflictingMethods.put(name, list);
    }
    // 往Method List中添加Method
    list.add(method);
  }

(4)調(diào)用 #resolveGetterConflicts(Map<String, List<Method>>) 方法解決簽名不同但是方法重復(fù)的 getter 方法的問(wèn)題屿储,一般屬性的 getter 只需要有一個(gè)即可,詳解參見(jiàn)下面對(duì)該方法的介紹渐逃。

4、getClassMethods(Class<?> cls)

【功能】獲取類cls及其超類中聲明的方法民褂,及其實(shí)現(xiàn)的接口方法茄菊,使用本方法而不是更簡(jiǎn)單的 Class.getMethods() 是因?yàn)槲覀円蚕氆@取被聲明為 private 的方法。
【源碼與注釋】

  private Method[] getClassMethods(Class<?> cls) {
    // (1)用于記錄指定類中定義的全部方法的唯一簽名以及對(duì)應(yīng)的Method對(duì)象
    Map<String, Method> uniqueMethods = new HashMap<String, Method>();
    // (2)表示當(dāng)前在while循環(huán)體中被解析聲明方法所屬的類赊堪,將從cls到cls的父類面殖,最終為Object類
    Class<?> currentClass = cls;
    while (currentClass != null) {
      // (3)記錄currentClass這個(gè)類中定義的全部方法
      // PS.只能獲取類內(nèi)定義的普通方法,對(duì)于繼承的方法無(wú)法通過(guò)反射獲取
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

      // (4)我們也要尋找接口方法哭廉,因?yàn)轭惪赡苁浅橄蟮募沽牛赡軐?shí)現(xiàn)了接口方法,這些方法無(wú)法通過(guò)上面的反射獲取
      // we also need to look for interface methods -
      // because the class may be abstract
      Class<?>[] interfaces = currentClass.getInterfaces();
      for (Class<?> anInterface : interfaces) {
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
      }

      // (5)獲取父類,繼續(xù)循環(huán)辽幌,獲取從父類繼承的方法
      currentClass = currentClass.getSuperclass();
    }

    // (6)將 uniqueMethods 中的值即方法取出來(lái)放 methods 集合中
    Collection<Method> methods = uniqueMethods.values();

    // (7)將 methods 集合轉(zhuǎn)化為 Method 數(shù)組返回
    return methods.toArray(new Method[methods.size()]);
  }

【解析】
(1)定義局部變量 uniqueMethods:存儲(chǔ)方法簽名和方法的映射關(guān)系增淹,方法簽名可以看成是每個(gè)方法的唯一標(biāo)識(shí),其組成規(guī)則可以看下面關(guān)于 #addUniqueMethods() 方法的詳解乌企。
(2)定義局部變量 currentClass:表示當(dāng)前被解析方法的類虑润,將一直沿著繼承鏈向上循環(huán),從cls到cls的父類加酵,最終為Object類拳喻。
(3)調(diào)用 #addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) 方法記錄當(dāng)前類中定義的所有方法到 uniqueMethods 中,其源碼實(shí)現(xiàn)如下:

  private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
      // (3.1)過(guò)濾掉橋接方法猪腕,橋接方法不是類中定義的方法冗澈,而是編譯器為了兼容自動(dòng)生成的方法
      if (!currentMethod.isBridge()) {
        // (3.2)獲取方法簽名
        // 通過(guò)Reflector.getSignature()方法得到的方法簽名是: 返回值類型#方法名稱:參數(shù)類型列表。
        // 例如陋葡,Reflector.getSignature(Method)方法的唯一簽名是:
        // java.lang.String#getSignature:java.lang.reflect.Method
        // 通過(guò)Reflector.getSignature()方法得到的方法簽名是全局唯一的亚亲,可以作為該方法的唯一標(biāo)識(shí)
        String signature = getSignature(currentMethod);

        // (3.3)檢測(cè)在子類中是否已經(jīng)添加過(guò)該方法,如果在子類中已經(jīng)添加過(guò)脖岛,則表示子類覆蓋了該方法朵栖,
        // 無(wú)須再想uniqueMethods集合中添加該方法了
        // check to see if the method is already known
        // if it is known, then an extended class must have
        // overridden a method
        if (!uniqueMethods.containsKey(signature)) {
          if (canAccessPrivateMethods()) {
            try {
              currentMethod.setAccessible(true);
            } catch (Exception e) {
              // Ignored. This is only a final precaution, nothing we can do.
            }
          }
          // (3.4)記錄該簽名和方法的對(duì)應(yīng)關(guān)系
          uniqueMethods.put(signature, currentMethod);
        }
      }
    }
  }
  • (3.1)首先過(guò)濾掉橋接方法,所謂橋接方法柴梆,就是編譯器為了兼容JDK自動(dòng)生成而不是代碼中定義的方法
  • (3.2)獲取方法簽名陨溅,源碼如下:
  private String getSignature(Method method) {
    StringBuilder sb = new StringBuilder();
    // 返回類型
    Class<?> returnType = method.getReturnType();
    if (returnType != null) {
      sb.append(returnType.getName()).append('#');
    }
    // 方法名
    sb.append(method.getName());
    // 方法參數(shù)類型
    Class<?>[] parameters = method.getParameterTypes();
    for (int i = 0; i < parameters.length; i++) {
      if (i == 0) {
        sb.append(':');
      } else {
        sb.append(',');
      }
      sb.append(parameters[i].getName());
    }
    return sb.toString();
  }

其組成規(guī)則為:方法返回值類型#方法名:參數(shù)1類型,參數(shù)2類型,參數(shù)3類型,...,參數(shù)名n類型
eg:void#addUniqueMethods:Map<String, Method>,Method[]

  • (3.3)因?yàn)樽宇惪赡苤貙?xiě)父類的方法,因此當(dāng)前類為上一次循環(huán)類的父類時(shí)绍在,有可能父類被子類重寫(xiě)的方法的簽名跟子類是一樣的门扇,這種情況以子類中聲明的方法為準(zhǔn),如果方法是私有的偿渡,則添加訪問(wèn)權(quán)限臼寄。
  • (3.4)將方法簽名和方法的映射添加到 uniqueMethods 中。

(4)獲取當(dāng)前類實(shí)現(xiàn)的接口溜宽,并遍歷接口調(diào)用 #addUniqueMethods(Map<String, Method>, Method[]) 獲得接口中定義的方法吉拳。
(5)獲取父類,繼續(xù)循環(huán)适揉,獲取從父類繼承的方法留攒。
(6)循環(huán)結(jié)束后,將 uniqueMethods 中的值即獲取到的類的所有方法放到 methods 集合中嫉嘀。
(7)將 methods 集合轉(zhuǎn)化為 Method 數(shù)組返回炼邀。

5、resolveGetterConflicts(Map<String, List<Method>> conflictingGetters)

【功能】解決一個(gè)屬性有多個(gè) getter 方法的問(wèn)題剪侮,并調(diào)用 #addGetMethod(String name, Method method) 方法真正填充 getMethodsgetTypes 集合拭宁。
【源碼與注釋】

  private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    // (1)對(duì)每個(gè)屬性的方法逐個(gè)處理
    for (String propName : conflictingGetters.keySet()) {
      List<Method> getters = conflictingGetters.get(propName);
      Iterator<Method> iterator = getters.iterator();
      Method firstMethod = iterator.next();
      // (2)該字段只有一個(gè)getter方法,沒(méi)有沖突,直接添加到getMethods集合并填充getTypes集合
      if (getters.size() == 1) {
        addGetMethod(propName, firstMethod);
      } else {
        // (3)同一屬性名稱存在多個(gè)getter方法杰标,則需要對(duì)比這些getter方法的返回值兵怯,選擇getter方法
        // 迭代過(guò)程中的臨時(shí)變量,用于記錄迭代到目前為止在旱,最適合作為getter方法的Method
        Method getter = firstMethod;
        // (3.1)記錄返回值類型
        Class<?> getterType = firstMethod.getReturnType();
        while (iterator.hasNext()) {
          Method method = iterator.next();
          Class<?> methodType = method.getReturnType();
          if (methodType.equals(getterType)) {
            // (3.2)返回值相同摇零,實(shí)際上沒(méi)有沖突,證明方法簽名生成那個(gè)可能有問(wèn)題了桶蝎,拋出異常
            throw new ReflectionException("Illegal overloaded getter method with ambiguous type for property "
                + propName + " in class " + firstMethod.getDeclaringClass()
                + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
          } else if (methodType.isAssignableFrom(getterType)) {
            // (3.3)假如判斷結(jié)果為true驻仅,表示methodType是getterType的繼承父類或?qū)崿F(xiàn)的接口
            // 當(dāng)前最合適的方法(getter)的返回值是當(dāng)前方法(method)返回值的子類,什么都不做登渣,當(dāng)前最適合的方法依然不變
            // OK getter type is descendant
          } else if (getterType.isAssignableFrom(methodType)) {
            // (3.4)當(dāng)前方法的返回值是當(dāng)前最適合的方法的返回值的子類噪服,更新臨時(shí)變量getter,當(dāng)前的getter方法成為最適合的getter方法
            getter = method;
            getterType = methodType;
          } else {
            // (3.5)返回值相同胜茧,二義性粘优,拋出異常
            throw new ReflectionException("Illegal overloaded getter method with ambiguous type for property "
                + propName + " in class " + firstMethod.getDeclaringClass()
                + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
          }
        }
        // (4)while循環(huán)結(jié)束篩選出最合適的方法之后,調(diào)用addGetMethod()方法填充getMethods和getTypes集合
        addGetMethod(propName, getter);
      }
    }
  }

【解析】
(1)遍歷逐個(gè)處理每個(gè)屬性呻顽,因?yàn)樽宇惪梢愿采w重寫(xiě)父類的方法雹顺,所以一個(gè)屬性可能有多個(gè) getter 方法
(2)如果屬性只有一個(gè) getter 方法,沒(méi)有沖突廊遍,則直接調(diào)用 #addGetMethod(String name, Method method) 方法填充處理嬉愧。
(3)同一屬性可能有多個(gè) getter 方法,定義局部變量 getter 存儲(chǔ)當(dāng)前最合適的getter

  • (3.1)定義局部變量 getterType喉前,記錄 getter 的返回類型没酣。
  • (3.2)因?yàn)?getter 方法名相同,且不帶參數(shù)卵迂,所以如果子類和父類方法返回值類型完全相同裕便,說(shuō)明要么子類畫(huà)蛇添足方法跟父類相同如下所示,要么證明生成方法簽名的方法可能有問(wèn)題见咒,此時(shí)拋出異常偿衰。
// 父類
public class SupClass {
    private List userList;
    public List getUserList() {
        return userList;
    }
}
// 子類
public class SubClass {
    private List userList;
    public List getUserList() {
        return userList;
    }
}
  • (3.3)如果遍歷到的其他 getter 方法的返回值類型是當(dāng)前最適合的 getter 方法返回值類型的父類,則 getter 依然是最合適的方法改览,舉例如下:
// 父類
public class SupClass() {
    private List userList;
    // 當(dāng)前遍歷到的method哎垦,methodType為L(zhǎng)ist
    public List getUserList() {
        return userList;
    }
}
// 子類
public class SubClass {
    private ArrayList userList;
    // 當(dāng)前遍歷篩選到的最合適的getter,getterType為ArrayList
    public ArrayList getUserList() {
        return userList;
    }
}
  • (3.4)跟上面的情況反過(guò)來(lái)恃疯,只是此時(shí) getter 為父類的 getUserList 方法,getterTypeList墨闲,這時(shí)遍歷到更合適的子類的方法今妄,因?yàn)槠浞祷仡愋透泳唧w,所以更新替換 gettergetterType
  • (3.5)如果不同的 getter 只是剛好方法名相同盾鳞,實(shí)際上返回類型既不完全相同犬性,也沒(méi)有繼承派生關(guān)系,則證明開(kāi)發(fā)人員寫(xiě)的類不符合 JavaBean 的規(guī)范腾仅,此時(shí)直接拋出異常乒裆。
  • (4)找到最合適的 getter 方法之后,調(diào)用 #addGetMethod(String name, Method method) 方法填充處理推励。

6鹤耍、addGetMethod(String name, Method method)

【功能】篩選合法的屬性名,將方法封裝成 MethodInvoker 作為 value验辞,屬性名作為 key 填充 getMethods 集合稿黄;解析方法返回的真正類型(因?yàn)橛锌赡軒Х盒汀⑼ㄅ浞龋┳鳛?value跌造,屬性名作為 key 填充 getTypes 集合杆怕。
【源碼與注釋】

  private void addGetMethod(String name, Method method) {
    // (1)篩選過(guò)濾掉非法的屬性名
    if (isValidPropertyName(name)) {
      // (2)將屬性名以及對(duì)應(yīng)的MethodInvoker對(duì)象添加到getMethods集合中,MethodInvoker是對(duì)Method對(duì)象反射操作的封裝
      getMethods.put(name, new MethodInvoker(method));

      // (3)因?yàn)榉椒ǚ祷刂抵锌赡軒в蟹盒涂翘埃琫g: List<T>
      // 所以通過(guò)TypeParameterResolver.resolveReturnType()方法將類型信息解析到Type對(duì)象中
      Type returnType = TypeParameterResolver.resolveReturnType(method, type);

      // (4)將屬性名稱和對(duì)應(yīng)的返回值類型添加到getTypes集合中保存
      getTypes.put(name, typeToClass(returnType));
    }
  }

【解析】
(1)調(diào)用 #isValidPropertyName(String) 方法過(guò)濾出合理的屬性名陵珍,合理的標(biāo)準(zhǔn)是:

  • 屬性名不以 $ 開(kāi)頭 + 屬性名不為 serialVersionUID (類實(shí)現(xiàn)序列化接口要定義的屬性)+ 屬性名不為 class
    其源碼如下:
  private boolean isValidPropertyName(String name) {
    return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
  }

(2)根據(jù) method 對(duì)象封裝創(chuàng)建 MethodInvoker 作為 value,屬性名為 key 填充 getMethods 集合违施。
(3)調(diào)用 #TypeParameterResolver.resolveReturnType(Method method, Type srcType) 解析返回值的具體類型(關(guān)于該工具類的解析過(guò)程可參考:[MyBatis源碼分析 - 反射器模塊 - 組件四] TypeParameterResolver
)互纯,并調(diào)用 #typeToClass(Type src) 方法將返回類型對(duì)應(yīng)的 Type 對(duì)象轉(zhuǎn)化為 Class 對(duì)象并填充 getTypes 集合。醉拓,其源碼如下:

  private Class<?> typeToClass(Type src) {
    Class<?> result = null;
    // 普通類型伟姐,直接使用類
    if (src instanceof Class) {
      result = (Class<?>) src;
    // 參數(shù)化類型,使用原始類型亿卤,如參數(shù)化類型List<String>的原始類型是List
    } else if (src instanceof ParameterizedType) {
      result = (Class<?>) ((ParameterizedType) src).getRawType();
    // 泛型數(shù)據(jù)類型愤兵,形如A<T>[]或T[],前者的數(shù)組元素類型是ParameterizedType排吴,后者是TypeVariable
    // 當(dāng)然秆乳,經(jīng)過(guò)TypeParameterResolver的處理,里面的泛型已被解析為具體類型钻哩,如List<String>[]屹堰、String[]
    } else if (src instanceof GenericArrayType) {
      Type componentType = ((GenericArrayType) src).getGenericComponentType();
      // 數(shù)組元素已經(jīng)是一個(gè)具體類型,直接返回形如String[]對(duì)應(yīng)的Class對(duì)象即可
      if (componentType instanceof Class) {
        result = Array.newInstance((Class<?>) componentType, 0).getClass();
      } else {
        // 數(shù)組元素依然不是一個(gè)普通類型街氢,如List<String>[]扯键,遞歸調(diào)用typetoClass方法處理,如上會(huì)返回List
        // 最終返回的result為L(zhǎng)ist[]
        Class<?> componentClass = typeToClass(componentType);
        result = Array.newInstance((Class<?>) componentClass, 0).getClass();
      }
    }
    if (result == null) {
      result = Object.class;
    }
    return result;
  }


7珊肃、addSetMethods(Class<?> cls)

【功能】獲取類的 setter 方法荣刑,并提取屬性名馅笙,填充 setMethodssetTypes 集合。
【源碼與注解】

  private void addSetMethods(Class<?> cls) {
    // 屬性名與其 setter 方法的映射
    Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
    // 獲取類的所有方法
    Method[] methods = getClassMethods(cls);
    // 遍歷所有方法
    for (Method method : methods) {
      String name = method.getName();
      // 篩選出符合 setter 方法名規(guī)則的方法
      if (name.startsWith("set") && name.length() > 3) {
        // setter 方法的參數(shù)個(gè)數(shù)應(yīng)該為1
        if (method.getParameterTypes().length == 1) {
          // 根據(jù)方法名提取出屬性名厉亏,eg: getUser() => user
          name = PropertyNamer.methodToProperty(name);
          // 將屬性與其 setter 方法的映射添加到conflictingSetters中
          addMethodConflict(conflictingSetters, name, method);
        }
      }
    }
    // 解決同個(gè)屬性名有多個(gè)沖突的 setter 方法的問(wèn)題
    resolveSetterConflicts(conflictingSetters);
  }

【解析】
??跟 #addGetMethods(Class<?> cls) 方法的處理過(guò)程大同小異董习,setter 方法不管參數(shù)類型是否布爾值,方法名都形如 setXxx爱只,并且參數(shù)個(gè)數(shù)必須為1皿淋,然后提取屬性名,添加到 conflictingSetters 映射中恬试,最后調(diào)用 #resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) 方法處理 setter 方法沖突窝趣。

8、resolveSetterConflicts(Map<String, List<Method>> conflictingSetters)

【功能】為類的每個(gè)屬性挑選出最合適的 setter 方法忘渔,并調(diào)用 #addSetMethod(String name, Method method) 方法處理填充 setMethodssetTypes 集合高帖。
【源碼與注解】

  private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    // (1)逐個(gè)處理每個(gè)屬性,為其找到最合適的 setter 方法畦粮,因?yàn)樽宇惪梢愿采w父類的方法散址,所以一個(gè)屬性可能有多個(gè) setter 方法
    for (String propName : conflictingSetters.keySet()) {
      List<Method> setters = conflictingSetters.get(propName);
      Method firstMethod = setters.get(0);
      // (2)只有一個(gè) setter 方法,直接調(diào)用addSetMethod方法填充setMethods和setTypes集合
      if (setters.size() == 1) {
        addSetMethod(propName, firstMethod);
      } else {
        // (3)獲取正在篩選 setter 方法的屬性的 getter 方法的返回類型宣赔,應(yīng)該跟 setter 的參數(shù)類型一致
        Class<?> expectedType = getTypes.get(propName);
        // (3.1)屬性有setter卻沒(méi)有g(shù)etter方法预麸,存在二義性,不符合JavaBeans的規(guī)范儒将,直接拋出異常
        if (expectedType == null) {
          throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
              + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
              "specification and can cause unpredicatble results.");
        } else {
          Iterator<Method> methods = setters.iterator();
          Method setter = null;
          while (methods.hasNext()) {
            Method method = methods.next();
            // (3.2)setter參數(shù)個(gè)數(shù)應(yīng)該只有一個(gè)并且參數(shù)類型跟getter方法的返回值類型相同吏祸,不符合則循環(huán)
            if (method.getParameterTypes().length == 1
                && expectedType.equals(method.getParameterTypes()[0])) {
              setter = method;
              break;
            }
          }
          // (4)如果循環(huán)結(jié)束還沒(méi)找到setter方法,則拋出異常
          if (setter == null) {
            throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
                + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
                "specification and can cause unpredicatble results.");
          }
          // (5)while循環(huán)結(jié)束篩選出最合適的方法之后钩蚊,調(diào)用addSetMethod()方法填充setMethods和setTypes集合
          addSetMethod(propName, setter);
        }
      }
    }
  }

【詳解】
(1)(2)步的處理與 #resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) 類似贡翘。
(3)一個(gè)符合 JavaBean 規(guī)范的類應(yīng)該同時(shí)有 getter 和 setter,并且 getter 的返回類型和 setter 的參數(shù)類型一致砰逻;程序先去處理 getter鸣驱,所以這里先根據(jù)屬性名從 getTypes 獲取到 getter 的返回類型。

  • (3.1)如果返回類型為空蝠咆,證明該屬性沒(méi)有 getter 方法踊东,這里拋出異常。
  • (3.2)如果不為空刚操,則遍歷所有的 setter闸翅,找到參數(shù)個(gè)數(shù)為1并且參數(shù)類型與 getter 返回類型相同的方法,找到即賦值給變量 setter 菊霜,并跳出循環(huán)坚冀。
    (4)如果沒(méi)找到符合規(guī)范的 setter 方法,則拋出異常鉴逞。
    (5)調(diào)用 #addSetMethod(String name, Method method) 方法真正填充 setMethodssetTypes 集合遗菠,其代碼很簡(jiǎn)單联喘,同樣跟上面的 #addGetMethod(String name, Method method) 類似,源碼如下:
  private void addSetMethod(String name, Method method) {
    // 篩選過(guò)濾掉非法的屬性名
    if (isValidPropertyName(name)) {
      // 將屬性名以及對(duì)應(yīng)的MethodInvoker對(duì)象添加到setMethods集合中辙纬,MethodInvoker是對(duì)Method對(duì)象反射操作的封裝
      setMethods.put(name, new MethodInvoker(method));

      // 因?yàn)榉椒ǚ祷刂抵锌赡軒в蟹盒停琫g: List<T>
      // 所以通過(guò)TypeParameterResolver.resolveReturnType()方法將類型信息解析到Type對(duì)象中
      Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);

      // 將屬性名稱和對(duì)應(yīng)的返回值類型添加到setTypes集合中保存
      setTypes.put(name, typeToClass(paramTypes[0]));
    }
  }


9叭喜、addFields(Class<?> clazz)

【功能】對(duì)于沒(méi)有 getter 和 setter 的屬性贺拣,通過(guò)本方法處理,填充到 getMethods捂蕴、getTypes譬涡、setMethodssetTypes集合中啥辨,在獲取設(shè)置這些屬性值時(shí)涡匀,是通過(guò) Field 對(duì)象的反射方法 getset實(shí)現(xiàn)的溉知,是對(duì) #addSetMethods(...)#addGetMethods(...) 方法的補(bǔ)充陨瘩。
【源碼與注解】

  private void addFields(Class<?> clazz) {
    // (1)獲取類中聲明的所有field屬性,包含了已經(jīng)解析出來(lái)的有 getter&setter 的屬性
    Field[] fields = clazz.getDeclaredFields();
    // 遍歷field屬性
    for (Field field : fields) {
      // 設(shè)置私有屬性訪問(wèn)權(quán)限
      if (canAccessPrivateMethods()) {
        try {
          field.setAccessible(true);
        } catch (Exception e) {
          // Ignored. This is only a final precaution, nothing we can do.
        }
      }
      if (field.isAccessible()) {
        // (2)過(guò)濾掉已經(jīng)有 getter 和 setter 的屬性
        if (!setMethods.containsKey(field.getName())) {
          // (2.1)獲取類的修飾符级乍,過(guò)濾掉被聲明為static final的屬性舌劳,這些屬性只能被類加載器設(shè)置其初始值
          int modifiers = field.getModifiers();
          if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
            // (2.2)
            addSetField(field);
          }
        }
        if (!getMethods.containsKey(field.getName())) {
          // (2.3)
          addGetField(field);
        }
      }
    }
    // (3)獲取父類,找出父類中聲明的屬性
    if (clazz.getSuperclass() != null) {
      addFields(clazz.getSuperclass());
    }
  }

【詳解】
(1)獲取類中聲明的所有field屬性玫荣,包含了已經(jīng)解析出來(lái)的有 getter&setter 的屬性甚淡。
(2)過(guò)濾掉已經(jīng)有 getter&setter 的屬性。

  • (2.1)過(guò)濾掉被聲明為 static final 的屬性捅厂,這類屬性只允許被類加載器初始化贯卦。
  • (2.2)調(diào)用 #addSetField(Field field) 填充 setMethodssetTypes 集合,源碼如下:
  private void addSetField(Field field) {
    // 過(guò)濾掉非法的屬性
    if (isValidPropertyName(field.getName())) {
      // 創(chuàng)建封裝了屬性對(duì)應(yīng)Field對(duì)象的SetFieldInvoker對(duì)象焙贷,內(nèi)部利用Field的set方法來(lái)反射設(shè)置屬性值
      setMethods.put(field.getName(), new SetFieldInvoker(field));
      // 解析獲取屬性值的具體類型(如果帶泛型或通配符)
      Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
      // 將屬性和屬性值類型對(duì)應(yīng)的Class對(duì)象映射填充setTypes集合
      setTypes.put(field.getName(), typeToClass(fieldType));
    }
  }
  • (2.3)調(diào)用 #addGetField(Field field) 填充 getMethodsgetTypes 集合撵割,源碼如下:
  private void addGetField(Field field) {
    // 過(guò)濾掉非法的屬性
    if (isValidPropertyName(field.getName())) {
      // 創(chuàng)建封裝了屬性對(duì)應(yīng)Field對(duì)象的GetFieldInvoker對(duì)象,內(nèi)部利用Field的get方法來(lái)反射設(shè)置屬性值
      getMethods.put(field.getName(), new GetFieldInvoker(field));
      // 解析獲取屬性值的具體類型(如果帶泛型或通配符)
      Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
      // 將屬性和屬性值類型對(duì)應(yīng)的Class對(duì)象映射填充setTypes集合
      getTypes.put(field.getName(), typeToClass(fieldType));
    }
  }

(3)獲取父類盈厘,通過(guò)遞歸調(diào)用本方法找出父類中聲明的屬性睁枕。

10、其他方法

【功能】主要是對(duì) Reflector 類中的屬性的使用沸手,對(duì)外暴露使用的方法外遇。

  • (1)#hasDefaultConstructor():判斷被解析的類是否有構(gòu)造方法。
  • (2)#getSetInvoker(String propertyName):根據(jù)屬性名獲取對(duì)應(yīng)的 Invoker 對(duì)象契吉,可通過(guò)該對(duì)象設(shè)置屬性值跳仿。
  • (3)#getGetInvoker(String propertyName):根據(jù)屬性名獲取對(duì)應(yīng)的 Invoker 對(duì)象,可通過(guò)該對(duì)象獲取屬性值捐晶。
  • (4)#getSetterType(String propertyName):根據(jù)屬性名獲取對(duì)應(yīng)的 setter 方法的參數(shù)類型對(duì)應(yīng)的 Class 對(duì)象菲语。
  • (5)#getGetterType(String propertyName):根據(jù)屬性名獲取對(duì)應(yīng)的 getter 方法的返回值類型對(duì)應(yīng)的 Class 對(duì)象妄辩。
  • (6)#getGetablePropertyNames():獲取類的所有可讀屬性。
  • (7)#getSetablePropertyNames():獲取類的所有可寫(xiě)屬性山上。
  • (8)#hasSetter(String propertyName):判斷傳入的屬性名是否有 setter眼耀,可用來(lái)判斷被解析的類是否有某屬性或是否可設(shè)置某屬性。
  • (9)#hasGetter(String propertyName):判斷傳入的屬性名是否有 getter佩憾,可用來(lái)判斷被解析的類是否有某屬性或是否可讀取某屬性哮伟。
  • (10)#findPropertyName(String name):根據(jù)傳入不區(qū)分大小寫(xiě)的屬性名獲取實(shí)際真實(shí)的屬性名。


五妄帘、ReflectorFactory

??ReflectorFactory 是一個(gè)接口楞黄,定義了創(chuàng)建和緩存 Reflector 對(duì)象的方法,只有一個(gè)實(shí)現(xiàn)類 DefaultReflectorFactory抡驼,類關(guān)系圖如下:


??ReflectorFactory 接口定義如下:

public interface ReflectorFactory {
  // 判斷和設(shè)置是否有緩存Reflector對(duì)象
  boolean isClassCacheEnabled();

  void setClassCacheEnabled(boolean classCacheEnabled);

  // 根據(jù)傳入的Class對(duì)象鬼廓,創(chuàng)建對(duì)應(yīng)的Reflector對(duì)象
  Reflector findForClass(Class<?> type);
}

【解析】
??#isClassCacheEnabled()#setClassCacheEnabled() 方法提供了調(diào)用代碼Reflector工廠對(duì)象是否有緩存生成的 Reflector 對(duì)象的信息致盟,#findForClass(Class<?> type) 方法則是根據(jù)傳入的 Class 對(duì)象創(chuàng)建 Reflector 對(duì)象碎税。

??唯一一個(gè)實(shí)現(xiàn)類 DefaultReflectorFactory 的源碼如下:

public class DefaultReflectorFactory implements ReflectorFactory {
  // 判斷是否要緩存Reflector對(duì)象的標(biāo)志位
  private boolean classCacheEnabled = true;
  // Reflector對(duì)象緩存
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    // 如果有緩存,則先嘗試從緩存中取勾邦,如果緩存中沒(méi)有則先創(chuàng)建Reflector對(duì)象蚣录,再放入緩存,最后返回
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      Reflector cached = reflectorMap.get(type);
      if (cached == null) {
        cached = new Reflector(type);
        reflectorMap.put(type, cached);
      }
      return cached;
    } else {
      // 如果沒(méi)有緩存眷篇,直接創(chuàng)建Reflector對(duì)象即可
      return new Reflector(type);
    }
  }
}

【解析】
??代碼比較簡(jiǎn)單萎河,通過(guò)一個(gè)布爾型的標(biāo)志位 classCacheEnabled 判斷是否要對(duì)生成的 Reflector 對(duì)象進(jìn)行緩存,前兩個(gè)接口方法的實(shí)現(xiàn)也依賴于這個(gè)標(biāo)志位蕉饼;定義了一個(gè)并發(fā)容器 reflectorMap 緩存 Class 對(duì)象及 Reflector 對(duì)象的映射關(guān)系虐杯,將并發(fā)控制委托給 ConcurrentMap,#findForClass() 方法中昧港,如果有緩存擎椰,先從緩存中取,如果不為空直接返回创肥,如果為空則先創(chuàng)建再緩存最后返回达舒,如果沒(méi)緩存,則每次都直接創(chuàng)建返回叹侄。


六巩搏、測(cè)試案例

??先來(lái)個(gè)簡(jiǎn)單的,不帶繼承關(guān)系趾代,不帶泛型的JavaBean贯底,如下:

public class ReflectorTest {
    // 簡(jiǎn)單Java類
    static class SimpleJavaBean {
        private Long id;
        private String username;
        private double money;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        @Override
        public String toString() {
            return "{ id:" + id + ", username:" + username + ", money:" + money + " }";
        }
    }
}

??測(cè)試案例覆蓋了 Reflector 對(duì)外暴露的所有方法,單元測(cè)試代碼如下:

    @Test
    public void testSimpleJavaBean() throws InvocationTargetException, IllegalAccessException {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(SimpleJavaBean.class);
        Assert.assertTrue(reflector.hasDefaultConstructor());

        String[] readablePropertyNames = reflector.getGetablePropertyNames();
        Assert.assertEquals(3, readablePropertyNames.length);
        // 輸出 money id username撒强,雖然money沒(méi)有顯式的getter禽捆,但是可以通過(guò)反射獲取笙什,所以也是可讀屬性
        System.out.println("[readablePropertyNames] ");
        for (String name : readablePropertyNames)
            System.out.print(name + " ");

        System.out.println();
        String[] writeablePropertyNames = reflector.getSetablePropertyNames();
        Assert.assertEquals(3, writeablePropertyNames.length);
        System.out.println("[writeablePropertyNames]");
        for (String name : writeablePropertyNames)
            System.out.print(name + " ");
        System.out.println();

        Assert.assertEquals(Long.class, reflector.getGetterType("id"));
        Assert.assertEquals(String.class, reflector.getSetterType("username"));
        Assert.assertEquals(double.class, reflector.getGetterType("money"));

        Assert.assertTrue(reflector.hasGetter("id"));
        Assert.assertTrue(reflector.hasSetter("money"));

        Assert.assertEquals("id", reflector.findPropertyName("ID"));
        Assert.assertEquals("username", reflector.findPropertyName("Username"));

        SimpleJavaBean bean = new SimpleJavaBean();
        System.out.println("bean(before call Invoker): " + bean);
        Invoker idSetInvoker = reflector.getSetInvoker("id");
        idSetInvoker.invoke(bean, new Object[]{100L});
        Invoker idGetInvoker = reflector.getGetInvoker("id");
        Assert.assertEquals(100L, idGetInvoker.invoke(bean, new Object[] {}));
        Invoker usernameSetInvoker = reflector.getSetInvoker("username");
        usernameSetInvoker.invoke(bean, new Object[]{"zhangsan"});
        Invoker moneySetInvoker = reflector.getSetInvoker("money");
        moneySetInvoker.invoke(bean, new Object[]{2.33});
        System.out.println("bean(after call Invoker): " + bean);
    }

??執(zhí)行結(jié)果如下:



??復(fù)雜一點(diǎn),經(jīng)過(guò)上面的源碼分析可知胚想,解析類屬性時(shí)不僅會(huì)解析類中定義的琐凭,還會(huì)通過(guò)解析接口和父類獲取 getter/setter 方法,最后解析出屬性名和屬性類型顿仇,所以定義一個(gè)測(cè)試接口和實(shí)現(xiàn)了接口的抽象父類淘正,最后被測(cè)試的類繼承了抽象父類,代碼如下:

    // 接口定義
    static interface Entity<T> {
        T getId();
        void setId(T id);
    }

    // 抽象父類實(shí)現(xiàn)了接口
    static abstract class AbstractEntity implements Entity<Long> {
        private Long id;

        @Override
        public Long getId() {
            return id;
        }

        @Override
        public void setId(Long id) {
            this.id = id;
        }
    }

    // 類繼承了父類臼闻,間接實(shí)現(xiàn)了接口
    static class Section extends AbstractEntity {
    }

??測(cè)試代碼聚焦是否能獲取到繼承的屬性和正確解析類型,如下:

    @Test
    public void testGetSetterType() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Section.class);
        Assert.assertEquals(Long.class, reflector.getSetterType("id"));
    }

    @Test
    public void testGetGetterType() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Section.class);
        Assert.assertEquals(Long.class, reflector.getGetterType("id"));
    }

    @Test
    public void shouldNotGetClass() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Section.class);
        Assert.assertFalse(reflector.hasGetter("class"));
    }

??執(zhí)行結(jié)果:



??更復(fù)雜一些囤采,屬性的類型可能是比較復(fù)雜的類型述呐,比如鏈表List、數(shù)組蕉毯,屬性可能是私有的乓搬,一般帶泛型的類是設(shè)計(jì)用來(lái)復(fù)用的,其中子類繼承帶泛型的父類并且指定類型是一種常用的形式代虾,所以類定義如下:

  static abstract class Parent<T extends Serializable> {
    protected T id;
    protected List<T> list;
    protected T[] array;
    private T fld;
    public T pubFld;
    public T getId() {
      return id;
    }
    public void setId(T id) {
      this.id = id;
    }
    public List<T> getList() {
      return list;
    }
    public void setList(List<T> list) {
      this.list = list;
    }
    public T[] getArray() {
      return array;
    }
    public void setArray(T[] array) {
      this.array = array;
    }
    public T getFld() {
      return fld;
    }
  }

  static class Child extends Parent<String> {
  }

??測(cè)試代碼如下:

    // 測(cè)試是否能解析出泛型
    @Test
    public void shouldResolveSetterParam() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        assertEquals(String.class, reflector.getSetterType("id"));
    }

    // 測(cè)試解析復(fù)雜的類型鏈表
    @Test
    public void shouldResolveParameterizedSetterParam() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        assertEquals(List.class, reflector.getSetterType("list"));
    }

    // 測(cè)試解析復(fù)雜的類型數(shù)組
    @Test
    public void shouldResolveArraySetterParam() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        Class<?> clazz = reflector.getSetterType("array");
        assertTrue(clazz.isArray());
        assertEquals(String.class, clazz.getComponentType());
    }

    @Test
    public void shouldResolveGetterType() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        assertEquals(String.class, reflector.getGetterType("id"));
    }

    @Test
    public void shouldResolveSetterTypeFromPrivateField() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        assertEquals(String.class, reflector.getSetterType("fld"));
    }

    @Test
    public void shouldResolveGetterTypeFromPublicField() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        assertEquals(String.class, reflector.getGetterType("pubFld"));
    }

    @Test
    public void shouldResolveParameterizedGetterType() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        assertEquals(List.class, reflector.getGetterType("list"));
    }

    @Test
    public void shouldResolveArrayGetterType() throws Exception {
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        Reflector reflector = reflectorFactory.findForClass(Child.class);
        Class<?> clazz = reflector.getGetterType("array");
        assertTrue(clazz.isArray());
        assertEquals(String.class, clazz.getComponentType());
    }

??執(zhí)行結(jié)果:


??通過(guò)上面幾個(gè)測(cè)試案例进肯,可以比較好地學(xué)習(xí)掌握 Reflector 的用法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棉磨,一起剝皮案震驚了整個(gè)濱河市江掩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乘瓤,老刑警劉巖环形,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異衙傀,居然都是意外死亡抬吟,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門统抬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)火本,“玉大人,你說(shuō)我怎么就攤上這事聪建「婆希” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵妆偏,是天一觀的道長(zhǎng)刃鳄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)钱骂,這世上最難降的妖魔是什么叔锐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任午绳,我火速辦了婚禮,結(jié)果婚禮上锣夹,老公的妹妹穿的比我還像新娘是嗜。我一直安慰自己,他們只是感情好步责,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布返顺。 她就那樣靜靜地躺著,像睡著了一般蔓肯。 火紅的嫁衣襯著肌膚如雪遂鹊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天蔗包,我揣著相機(jī)與錄音秉扑,去河邊找鬼。 笑死调限,一個(gè)胖子當(dāng)著我的面吹牛舟陆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耻矮,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼秦躯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了裆装?” 一聲冷哼從身側(cè)響起踱承,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎米母,沒(méi)想到半個(gè)月后勾扭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铁瞒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年妙色,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慧耍。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡身辨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芍碧,到底是詐尸還是另有隱情煌珊,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布泌豆,位于F島的核電站定庵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蔬浙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一猪落、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧畴博,春花似錦笨忌、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至亮隙,卻和暖如春途凫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溢吻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工颖榜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人煤裙。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像噪漾,于是被迫代替她去往敵國(guó)和親硼砰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359