Mybatis源碼學習第四課---基礎支持層反射模塊分析

   MyBatis 在進行參數處理、結果映射時等操作時,會涉及大量的反射操作。爲了簡化這些反射相關操作,MyBatisorg.apache.ibatis.reflection 包下提供了專門的反射模塊,對反射操作做了近一步封裝,提供了更爲簡潔的 API

一、JavaBean規範

JavaBean 具有如下特徵:

  1. 所有的屬性都是私有的(通過 getter 和 setter 訪問)
  2. 擁有公有的無參構造函數
  3. 提供 setter/getter
  4. 實現 Serializable 接口

二、Reflector 和 ReflectorFactory

   2.1 Reflector 

         出於性能方面的考慮,Mybatis 不是等到使用的時候去解析 XML/反射類,而是爲每一個類提供了反射器類 Reflector ,該類中存儲了反射需要使用的類的元信息

        MyBatis 提供 Reflector 類來緩存類的字段名和 getter/setter 方法的元信息,使得反射時有更好的性能。使用方式是將原始類對象傳入其構造方法,生成 Reflector 對象。

     Reflector 是 MyBatis 中反射模塊的基礎,每個 Reflector 對象都對應一個類,在 Reflector 中 緩存了反射操作需要使用的類的元信息。 Reflector 中各個字段的含義如下:

  private final Class<?> type;//對於class的類型
  //可讀屬性的名稱集合,存在get方法即可讀
  private final String[] readablePropertyNames;
  //可寫屬性的名稱集合,存在set方法即可寫
  private final String[] writablePropertyNames;
  //記錄了屬性相應的 setter 方法, key 是屬性名稱, value 是 Invoker 對象,它是對 setter 方法對應
  private final Map<String, Invoker> setMethods = new HashMap<>();
  //屬性相應的 getter 方法集合, key 是屬性名稱, value 也是 Invoker 對象
  private final Map<String, Invoker> getMethods = new HashMap<>();
  //記錄了屬性相應的 setter 方法的參數值類型, key 是屬性名稱, value 是 setter 方法的參數類型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  //記錄 了屬性相應的 getter 方法的返回位類型, key 是屬性名稱, value 是 getter 方法的返回位類型
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  //默認構造方法
  private Constructor<?> defaultConstructor;
  //記錄所有屬性的名稱集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

在 Reflector 的構造方法中會解析指定的 Class 對象,並填充上述集合,具體實現如下:

 public Reflector(Class<?> clazz) {
    type = clazz;//初始化type對象
    //查找 clazz 的默認構造方法(元參構造方法),具體實現是通過反射遙歷所有構造方法,代碼並不複雜
    addDefaultConstructor(clazz);
    //處理clazz中的get方法信息,填充getMethods、getTypes
    addGetMethods(clazz);
    //處理clazz中的set方法信息,填充setMethods、setTypes
    addSetMethods(clazz);
    //處理沒有get、set方法的屬性
    addFields(clazz);
    //根據get、set方法初始化可讀屬性集合和可寫屬性集合
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    //初始化 caseinsensitivePropertyMap 集合,其中記錄了所有大寫格式的屬性名稱
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

   

從上面類的屬性中,我們可以看出:

一個反射器 Reflector 對應着一個 Class 對象
記錄了默認構造函數
其餘的是屬性及其 setter/gettter 相關

對於一個屬性(只有有 setter/getter 才能被稱之爲屬性)
(1)如果是可讀的(有 getter 方法),則 Reflector 會將其及其方法處理後放入對應的可讀相關的集合中;
(2)如果是可寫的(有 setter 方法),則 Reflector 會將其及其方法處理後放入對應的可寫相關的集合中;
(3)最後使用 Map<String, String> caseInsensitivePropertyMap 記錄所有的屬性。
 

    addGetMethods 和 addSetMethods 分別獲取類的所有方法,從符合 getter/setter 規範的方法中解析出字段名,並記錄方法的參數類型、返回值類型等信息:

  private void addGetMethods(Class<?> clazz) {
    //字段名-get方法
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    //獲取類的所有方法,及其實現接口的方法,並根據方法簽名去重
    Method[] methods = getClassMethods(clazz);
    //取get 方法 條件是入參爲空,且已get開頭
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveGetterConflicts(conflictingGetters);
  }

  public static boolean isGetter(String name) {
    return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
  }
 private void addSetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingSetters = new HashMap<>();
    Method[] methods = getClassMethods(clazz);
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveSetterConflicts(conflictingSetters);
  }

     對 getter/setter 方法進行去重是通過類似 java.lang.String#getSignature:java.lang.reflect.Method 的方法簽名來實現的,如果子類在實現過程中,參數、返回值使用了不同的類型(使用原類型的子類),則會導致方法簽名不一致,同一字段就會對應不同的 getter/setter 方法,因此需要進行去重

  private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
      Method winner = null;
      // 屬性名
      String propName = entry.getKey();
      for (Method candidate : entry.getValue()) {
        if (winner == null) {
          winner = candidate;
          continue;
        }
        // 字段對應了多個get方法
        Class<?> winnerType = winner.getReturnType();
        Class<?> candidateType = candidate.getReturnType();
        if (candidateType.equals(winnerType)) {
          // 返回值類型相同
          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.");
          } else if (candidate.getName().startsWith("is")) {
            // 返回值爲boolean的get方法可能有多個,如getIsSave和isSave,優先取is開頭的
            winner = candidate;
          }
        } else if (candidateType.isAssignableFrom(winnerType)) {
          // OK getter type is descendant
          // 可能會出現接口中的方法返回值是List,子類實現方法返回值是ArrayList,使用子類返回值方法
        } else if (winnerType.isAssignableFrom(candidateType)) {
          winner = candidate;
        } 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.");
        }
      }
      // 記錄字段名對應的get方法對象和返回值類型
      addGetMethod(propName, winner);
    }
  }

    去重的方式是使用更規範的方法以及使用子類的方法。在確認字段名對應的唯一 getter/setter 方法後,記錄方法名對應的方法、參數、返回值等信息。MethodInvoker 可用於調用 Method 類的 invoke 方法來執行 getter/setter 方法(addSetMethods 記錄映射關係的方式與 addGetMethods 大致相同)。

private void addGetMethod(String name, Method method) {
  // 過濾$開頭、serialVersionUID的get方法和getClass()方法
  if (isValidPropertyName(name)) {
    // 字段名-對應get方法的MethodInvoker對象
    getMethods.put(name, new MethodInvoker(method));
    Type returnType = TypeParameterResolver.resolveReturnType(method, type);
    // 字段名-運行時方法的真正返回類型
    getTypes.put(name, typeToClass(returnType));
  }
}

接下來會執行 addFields 方法,此方法針對沒有 getter/setter 方法的字段,通過包裝爲 SetFieldInvoker 在需要時通過 Field 對象的反射來設置和讀取字段值。

private void addFields(Class<?> clazz) {
  Field[] fields = clazz.getDeclaredFields();
  for (Field field : fields) {
    if (!setMethods.containsKey(field.getName())) {
      // issue #379 - removed the check for final because JDK 1.5 allows
      // modification of final fields through reflection (JSR-133). (JGB)
      // pr #16 - final static can only be set by the classloader
      int modifiers = field.getModifiers();
      if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
        // 非final的static變量,沒有set方法,可以通過File對象做賦值操作
        addSetField(field);
      }
    }
    if (!getMethods.containsKey(field.getName())) {
      addGetField(field);
    }
  }
  if (clazz.getSuperclass() != null) {
    // 遞歸查找父類
    addFields(clazz.getSuperclass());
  }
}

2.2 Invoker

   Invoker 接口用於抽象設置和讀取字段值的操作。對於有 getter/setter 方法的字段,通過 MethodInvoker 反射執行;對應其它字段,通過 GetFieldInvokerSetFieldInvoker 操作 Field 對象的 getter/setter 方法反射執行。

public interface Invoker {
  /**
   * 通過反射設置或讀取字段值
   *
   * @param target
   * @param args
   * @return
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  /**
   * 字段類型
   *
   * @return
   */
  Class<?> getType();
}

                               å¨è¿éæå¥å¾çæè¿°

  • MethodInvoker:有 getter/setter 方法的字段,方法的 Invoker
  • GetFieldInvoker:如果沒有 getter,則使用該方法,通過 Filed 類直接讀取成員變量的值
  • SetFieldInvoker:如果沒有 setter,則使用該方法,通過 Filed 類直接設置成員變量的值

2.3 TypeParameterResolver

  在開始介紹 TypeParameterResolver 之前, 先簡單介紹一下 Type 接口的基礎知識。 Type 是所有類型的父接口,它有四個子接口和一個實現類。

  • Class:原始類型,Class 類的對象表示 JVM 中的一個類或接口, 每個 Java 類在 JVM 裏都表現爲一個 Class 對象。
  • ParameterizedType:泛型類型,如:List<String>
  • TypeVariable:泛型類型變量,如: List<T> 中的 T
  • GenericArrayType:組成元素是 ParameterizedTypeTypeVariable 的數組類型,如:List<String>[]T[]
  • WildcardType:通配符泛型類型變量,如:List<?> 中的

     在對 Reflector 的 分析過程中,我們看到了 TypeParameterResolver 的身影,它是一個工具類,提供了一系列靜態 方法來解析指定類中的宇段、方法返回值或方法參數的類型。 TypeParameterResolver 中各個靜 態方法之間的調用關係大致如圖  所示,爲保持清晰,其中遞歸調用沒有表現出來,在後面 的代碼分析過程中會進行強調,

   TypeParameterResolver 分別提供 resolveFieldTyperesolveReturnTyperesolveParamTypes 方法用於解析字段類型、方法返回值類型和方法入參類型,這些方法均調用 resolveType 來獲取類型信息:

 

/**
 * 獲取類型信息
 *
 * @param type 根據是否有泛型信息簽名選擇傳入泛型類型或簡單類型
 * @param srcType 引用字段/方法的類(可能是子類,字段和方法在父類聲明)
 * @param declaringClass 字段/方法聲明的類
 * @return
 */
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
  if (type instanceof TypeVariable) {
    // 泛型類型變量,如:List<T> 中的 T
    return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
  } else if (type instanceof ParameterizedType) {
    // 泛型類型,如:List<String>
    return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
  } else if (type instanceof GenericArrayType) {
    // TypeVariable/ParameterizedType 數組類型
    return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
  } else {
    // 原始類型,直接返回
    return type;
  }
}

 

   resolveTypeVar 用於解析泛型類型變量參數類型,如果字段或方法在當前類中聲明,則返回泛型類型的上界或 Object 類型;如   果在父類中聲明,則遞歸解析父類;父類也無法解析,則遞歸解析實現的接口

private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
  Type result;
  Class<?> clazz;
  if (srcType instanceof Class) {
    // 原始類型
    clazz = (Class<?>) srcType;
  } else if (srcType instanceof ParameterizedType) {
    // 泛型類型,如 TestObj<String>
    ParameterizedType parameterizedType = (ParameterizedType) srcType;
    // 取原始類型TestObj
    clazz = (Class<?>) parameterizedType.getRawType();
  } else {
    throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
  }

  if (clazz == declaringClass) {
    // 字段就是在當前引用類中聲明的
    Type[] bounds = typeVar.getBounds();
    if (bounds.length > 0) {
      // 返回泛型類型變量上界,如:T extends String,則返回String
      return bounds[0];
    }
    // 沒有上界返回Object
    return Object.class;
  }

  // 字段/方法在父類中聲明,遞歸查找父類泛型
  Type superclass = clazz.getGenericSuperclass();
  result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
  if (result != null) {
    return result;
  }

  // 遞歸泛型接口
  Type[] superInterfaces = clazz.getGenericInterfaces();
  for (Type superInterface : superInterfaces) {
    result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
    if (result != null) {
      return result;
    }
  }
  return Object.class;
}

通過調用 scanSuperTypes 實現遞歸解析:

private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
  if (superclass instanceof ParameterizedType) {
    // 父類是泛型類型
    ParameterizedType parentAsType = (ParameterizedType) superclass;
    Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
    // 父類中的泛型類型變量集合
    TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
    if (srcType instanceof ParameterizedType) {
      // 子類可能對父類泛型變量做過替換,使用替換後的類型
      parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
    }
    if (declaringClass == parentAsClass) {
      // 字段/方法在當前父類中聲明
      for (int i = 0; i < parentTypeVars.length; i++) {
        if (typeVar == parentTypeVars[i]) {
          // 使用變量對應位置的真正類型(可能已經被替換),如父類 A<T>,子類 B extends A<String>,則返回String
          return parentAsType.getActualTypeArguments()[i];
        }
      }
    }
    // 字段/方法聲明的類是當前父類的父類,繼續遞歸
    if (declaringClass.isAssignableFrom(parentAsClass)) {
      return resolveTypeVar(typeVar, parentAsType, declaringClass);
    }
  } else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
    // 父類是原始類型,繼續遞歸父類
    return resolveTypeVar(typeVar, superclass, declaringClass);
  }
  return null;
}

    解析方法返回值和方法參數的邏輯大致與解析字段類型相同,MyBatis 源碼的TypeParameterResolverTest 類提供了相關的測試用例。

2.4 ReflectorFactory

    看名稱,工廠方法,是爲了創建個緩存 Reflector。MyBatis 還提供 ReflectorFactory 接口用於實現 Reflector 容器,其默認實現爲 DefaultReflectorFactory,其中可以使用 classCacheEnabled 屬性來配置是否使用緩存。        

/**
 * 創建Reflector的工廠接口
 */
public interface ReflectorFactory {

  /**
   * 檢測 ReflectorFactory 對象是否會緩存 Reflector 對象
   */
  boolean isClassCacheEnabled();
  /**
   * 設置是否緩存
   */
  void setClassCacheEnabled(boolean classCacheEnabled);
  /**
   * 緩存中查找 Class 對應的 Reflector 對象,找不到則創建
   */
  Reflector findForClass(Class<?> type);
}

                                   å¨è¿éæå¥å¾çæè¿°

mybatis 爲我們提供了該方法的默認實現 DefaultReflectorFactory 。該類的實現很簡單,就是通過 ConcurrentMap<Class<?>, Reflector> 對 Reflector 進行緩存。

2.5 ObjectFactory

ObjectFactory 接口是 MyBatis 對象創建工廠,其默認實現 DefaultObjectFactory 通過構造器反射創建對象,支持使用無參構造器和有參構造器。

       MyBatis 中有很多模塊會使用到 ObjectFactory 接口,該接口提供了多個 create()方法的重載, 通過這些 create()方法可以創建指定類型的對象。 ObjectFactory 接口的定義如下:

public interface ObjectFactory {

  /**
   * 設置配置信息
   * @param properties
   */
  default void setProperties(Properties properties) {
    // NOP
  }

  //通過無參構造器創建對象
  <T> T create(Class<T> type);

  //根據參數列表,從指定類型中選擇合適的構造器創建對象
  <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

  ///檢測指定類型是否爲集合類型,主妥處理 java.util.Collection 及其子類
  <T> boolean isCollection(Class<T> type);

}

       DefaultObjectFactory是MyBatis提供的 ObjectFactory接口的唯一實現,它是一個反射工廠, 其 create()方法通過調用 instantiateClass()方法實現。 DefaultObjectFactory.instantiateClass()方法會 根據傳入的參數列表選擇合適的構造函數實例化對象,具體實現如下:

public class DefaultObjectFactory implements ObjectFactory, Serializable {

  private static final long serialVersionUID = -8855120656740914948L;

  //通過無參構造器創建對象
  @Override
  public <T> T create(Class<T> type) {
    return create(type, null, null);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    //對象類型
    Class<?> classToCreate = resolveInterface(type);
    // we know types are assignable
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }

  //最終創建對象的方法
  private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      //通過無參數構造器創建對象
      if (constructorArgTypes == null || constructorArgs == null) {
        //獲得構造器
        constructor = type.getDeclaredConstructor();
        try {
          //創建對象
          return constructor.newInstance();
        } catch (IllegalAccessException e) {
          if (Reflector.canControlMemberAccessible()) {
            constructor.setAccessible(true);
            return constructor.newInstance();
          } else {
            throw e;
          }
        }
      }
      //根據指定的參數列表查找構造函數,並實例化對象
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      try {
        return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
      } catch (IllegalAccessException e) {
        if (Reflector.canControlMemberAccessible()) {
          constructor.setAccessible(true);
          return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
        } else {
          throw e;
        }
      }
    } catch (Exception e) {
      String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
          .stream().map(Class::getSimpleName).collect(Collectors.joining(","));
      String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
          .stream().map(String::valueOf).collect(Collectors.joining(","));
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

  protected Class<?> resolveInterface(Class<?> type) {
    Class<?> classToCreate;
    if (type == List.class || type == Collection.class || type == Iterable.class) {
      classToCreate = ArrayList.class;
    } else if (type == Map.class) {
      classToCreate = HashMap.class;
    } else if (type == SortedSet.class) { // issue #510 Collections Support
      classToCreate = TreeSet.class;
    } else if (type == Set.class) {
      classToCreate = HashSet.class;
    } else {
      classToCreate = type;
    }
    return classToCreate;
  }

  @Override
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }

}

2.6 Property 工具集

     反射模塊中使用到的三個屬性工具類,分別是 PropertyTokenizer 、 PropertyNamer 、PropertyCopier。

  MyBatis 在映射文件定義 resultMap 支持如下形式:

<resultMap id="map" type="Order">
    <result property="orders[0].items[0].name" column="col1"/>
    <result property="orders[0].items[1].name" column="col2"/>
    ...
</resultMap>

rders[0].items[0].name 這樣的表達式是由 PropertyTokenizer 解析的,其構造方法能夠對錶達式進行解析;同時還實現了 Iterator 接口,能夠迭代解析表達式。

public PropertyTokenizer(String fullname) {
  // orders[0].items[0].name
  int delim = fullname.indexOf('.');
  if (delim > -1) {
    // name = orders[0]
    name = fullname.substring(0, delim);
    // children = items[0].name
    children = fullname.substring(delim + 1);
  } else {
    name = fullname;
    children = null;
  }
  // orders[0]
  indexedName = name;
  delim = name.indexOf('[');
  if (delim > -1) {
    // 0
    index = name.substring(delim + 1, name.length() - 1);
    // order
    name = name.substring(0, delim);
  }
}

  /**
   * 是否有children表達式繼續迭代
   *
   * @return
   */
  @Override
  public boolean hasNext() {
    return children != null;
  }

  /**
   * 分解出的 . 分隔符的 children 表達式可以繼續迭代
   * @return
   */
  @Override
  public PropertyTokenizer next() {
    return new PropertyTokenizer(children);
  }

    PropertyNamer 可以根據 getter/setter 規範解析字段名稱;PropertyCopier 則支持對有相同父類的對象,通過反射拷貝字段值。

   2.7 MetaClass

    MetaClass 通過 Reflector 和 PropertyTokenizer 組合使用, 實現了對複雜的屬性表達式的解 析,並實現了獲取指定屬性描述信息的功能。 

     Reflector 實現了實體類元信息的封裝,但是類中的成員變量是類的情況沒有進行處理。

     而 MetaClass 通過 ReflectorFactory 類型的成員變量,實現了實體類中成員變量是類情況的處理,通過與屬性工具類的結合,實現了對複雜表達式的解析和實現了獲取指定描述信息的功能。
   其成員變量:

public class MetaClass {
  //ReflectorFactory 對象,用於緩存 Reflector 對象
  private ReflectorFactory reflectorFactory;
  //在創建 MetaClass 時會指定一個類,該 Reflector 對象會用於記錄該類相關的元信息
  private Reflector reflector;

  MetaClass 的構造函數中會爲指定的 Class 創建相應的 Reflector 對象,井用其初始化 MetaClass.reflector 字段

   MetaClass 構造函數是私有的,通過靜態方法構建對象

 //私有化構造函數
  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    //創建reflector對象
    this.reflector = reflectorFactory.findForClass(type);
  }
  //使用靜態方法創建 MetaClass 對象 
  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }

    MetaClass 中比較重要的是 findProperty()方法,它是通過調用 MetaClass. buildProperty()方法 實現的,而 buildProperty()方法會通過 PropertyTokenizer解析複雜的屬性表達式,具體實現如下:

public String findProperty(String name) {
    ///委託給 buildProperty()方法實現 
    StringBuilder prop = buildProperty(name, new StringBuilder());
    return prop.length() > 0 ? prop.toString() : null;
  }

/**
   * 解析複雜的屬性表達式,例如:<result property="orders[0].items[0].name"column="item1"/> 中 property="orders[0].items[0].name"
   * 案例:
   * @param name tele.name
   * @param builder
   * @return
   */
  private StringBuilder buildProperty(String name, StringBuilder builder) {
    //構造兼解析,name:tele.name
    PropertyTokenizer prop = new PropertyTokenizer(name);//映射文件表達式迭代器
    if (prop.hasNext()) {
      //複雜表達式 //prop.getName():tele propertyName:tele
      String propertyName = reflector.findPropertyName(prop.getName());
      if (propertyName != null) {
        builder.append(propertyName);
        builder.append(".");
        // 加載內嵌字段類型對應的MetaClass
        MetaClass metaProp = metaClassForProperty(propertyName);
        //遞歸解析 PropertyTokenizer.children 字段,並將解析結果添加到 builder 中保存
        metaProp.buildProperty(prop.getChildren(), builder);
      }
    } else {
      String propertyName = reflector.findPropertyName(name);
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
  }

理解了這個方法(遞歸,該類中有很多類似的),就可以很好的對這個類進行理解,以查找(richType.richProperty)爲例:

  1. 通過 PropertyTokenizer 對錶達式進行解析, 得到當前的 name=richType, children=richProperty
  2. 從 reflector 中查找該 richType 屬性
  3. 將 richType 添加到 builder 中
  4. 使用 metaClassForProperty 創建 richType 的 MetaClass
  5. 遞歸調用自身來處理子表達式
  6. 退出的條件就是沒有子表達式。這個是爲了,我們類中有成員變量是類,我們可以通過其找到他們的所有類及其屬性。

注意,在此過程中,ReflectorFactory 一直是同一個,而其內部緩存了多個 Reflector 對象

2.8 ObjectWrapper


    MetaClass 是 MyBatis 對類級別的元信息的封裝和處理,下面來看 MyBatis 對對象級別的元 信息的處理。 ObjectWrapper 接口是對對象的包裝,抽象了對象的屬性信息,它定義了一系列查 詢對象屬性信息的方法,以及更新屬性的方法。 

     相對於 MetaClass 關注類信息,MetalObject 關注的是對象的信息,除了保存傳入的對象本身,還會爲對象指定一個 ObjectWrapper 將對象包裝起來。ObejctWrapper 體系如下:

              

     ObjectWrapper默認實現包括了對 MapCollection 和普通 JavaBean 的包裝MyBatis 還支持通過 ObjectWrapperFactory 接口對 ObejctWrapper 進行擴展,生成自定義的包裝類。MetaObject 對對象的具體操作,就委託給真正的 ObjectWrapper 處理。

public interface ObjectWrapper {

  //如果ObjectWrapper 中封裝的是普通的 Bean 對象,則調用相應屬性的相應 getter 方法,
  //如採封裝的是集合類,則獲取指定 key 或下標對應的 value 位
  Object get(PropertyTokenizer prop);

  //如果 ObjectWrapper 中封裝的是普通的 Bean 對象, 則調用相應屬性的相應 setter 方法,
   //  如果封裝的是集合類,則設置指定 key 或下標對應的 value 值
  void set(PropertyTokenizer prop, Object value);

  //查找屬性表達式指定的屬性,第二個參數表示是否忽略屬性表達式中的下畫線
  String findProperty(String name, boolean useCamelCaseMapping);

  String[] getGetterNames();//查找可寫屬性的名稱集合

  String[] getSetterNames();//查找可讀屬性的名稱集合
  //解析屬性表達式指定屬性的 setter 方法的參數類型
  Class<?> getSetterType(String name);//

  //解析屬性表達式指定屬性的 getter 方法的返回值類型
  Class<?> getGetterType(String name);

  //判斷屬性表達式指定屬性是否有 getter/setter 方法
  boolean hasSetter(String name);

  boolean hasGetter(String name);

  //爲屬性表達式指定的屬性創建相應的 MetaObject 對象 
  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);

  boolean isCollection();

  void add(Object element);

  <E> void addAll(List<E> element);

}

    BaseWrapper 是一個實現了 ObjectWrapper 接口的抽象類, 其中封裝了 MetaObject 對象,並 提供了三個常用的方法供其子類使用,

       

    BaseWrapper. resolveCollection()方法會調用 MetaObject. getValue()方法法,它會解析屬性表達 式井獲取指定的屬性, MetaObject.getValue()方法的實現在後面詳細介紹。 

    BaseWrapper.getCollectionValue()方法和 setCollectionValue()方法會解析屬性表達式的索引 信息,然後獲取/設置對應項。這兩個方法的實現類似,這裏只分析getCollectionValue ()方法


  protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
    if (collection instanceof Map) {//如果是 Map 類型,如l index 爲 key 
      return ((Map) collection).get(prop.getIndex());
    } else {
      int i = Integer.parseInt(prop.getIndex());//如果是其他集合類型,則 index 爲下標 
      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[]) {
       ..........................
      } else {
        throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
      }
    }
  }

    BeanWrapper 繼承了 BaseWrapper 抽象類,其中封裝了一個 JavaBean對象以及該 JavaBean 類相應的 MetaClass 對象,當然,還有從 BaseWrapper 繼承下來的、該 JavaBean 對象相應的 MetaObject 對象。

    例如賦值操作

  @Override
  public void set(PropertyTokenizer prop, Object value) {
    if (prop.getIndex() != null) {
      // 當前表達式是集合,如:items[0],就需要獲取items集合對象
      Object collection = resolveCollection(prop, object);
      // 在集合的指定索引上賦值
      setCollectionValue(prop, collection, value);
    } else {
      // 解析完成,通過Invoker接口做賦值操作
      setBeanProperty(prop, object, value);
    }
  }

  protected Object resolveCollection(PropertyTokenizer prop, Object object) {
    if ("".equals(prop.getName())) {
      return object;
    } else {
      // 在對象信息中查到此字段對應的集合對象
      return metaObject.getValue(prop.getName());
    }
  }

根據 PropertyTokenizer 對象解析出的當前字段是否存在 index 索引來判斷字段是否爲集合。如果當前字段對應集合,則需要在對象信息中查到此字段對應的集合對象:

public Object getValue(String name) {
  PropertyTokenizer prop = new PropertyTokenizer(name);
  if (prop.hasNext()) {
    // 如果表達式仍可迭代,遞歸尋找字段對應的對象
    MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
    if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
      return null;
    } else {
      return metaValue.getValue(prop.getChildren());
    }
  } else {
      // 字段解析完成
    return objectWrapper.get(prop);
  }
}

如果字段是簡單類型,BeanWrapper 獲取字段對應的對象邏輯如下:

@Override
public Object get(PropertyTokenizer prop) {
  if (prop.getIndex() != null) {
    // 集合類型,遞歸獲取
    Object collection = resolveCollection(prop, object);
    return getCollectionValue(prop, collection);
  } else {
    // 解析完成,反射讀取
    return getBeanProperty(prop, object);
  }
}

可以看到,仍然是會判斷表達式是否迭代完成,如果未解析完字段會不斷遞歸,直至找到對應的類型。前面說到 Reflector 創建過程中將對字段的讀取和賦值操作通過 Invoke 接口抽象出來,針對最終獲取的字段,此時就會調用 Invoke 接口對字段反射讀取對象值:

/**
 * 通過Invoker接口反射執行讀取操作
 *
 * @param prop
 * @param object
 */
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
  try {
    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) {
    throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);
  }
}

   對象讀取完畢再通過 setCollectionValue 方法對集合指定索引進行賦值或通過 setBeanProperty 方法對簡單類型反射賦值。MapWrapper 的操作與 BeanWrapper 大致相同,CollectionWrapper 相對更會簡單,只支持對原始集合對象進行添加操作。

   2.9 MetaObject

     ObjectWrapper 提供了獲取/設置對象中指 定的屬性值、檢測 ge出r/setter 等常用功能,但是 ObjectWrapper 只是這些功能的最後一站,我 們省略了對屬性表達式解析過程的介紹,而該解析過程是在 MetaObject 中實現的。 

MetaObject中的屬性如下:

  //原始 JavaBean 對象
  private final Object originalObject;
  //ObjectWrapper 對象,其中封裝了originalObject對象
  private final ObjectWrapper objectWrapper;
  //負責實例化 originalObject 的工廠對象,
  private final ObjectFactory objectFactory;
  //負責創建 ObjectWrapper 的工廠對象
  private final ObjectWrapperFactory objectWrapperFactory;
  //用於創建並緩存 Reflector 對象的工廠對象,
  private final ReflectorFactory reflectorFactory;

MetaObject的構造方法會根據傳入的原始對象的類型以及 ObjectFactory 工廠的實現,創建 相應的 ObjectWrapper 對象,代碼如下:

private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
  this.originalObject = object;
  this.objectFactory = objectFactory;
  this.objectWrapperFactory = objectWrapperFactory;
  this.reflectorFactory = reflectorFactory;

  // 根據傳入object類型不同,指定不同的wrapper
  if (object instanceof ObjectWrapper) {
    this.objectWrapper = (ObjectWrapper) object;
  } else if (objectWrapperFactory.hasWrapperFor(object)) {
    this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
  } else if (object instanceof Map) {
    this.objectWrapper = new MapWrapper(this, (Map) object);
  } else if (object instanceof Collection) {
    this.objectWrapper = new CollectionWrapper(this, (Collection) object);
  } else {
    this.objectWrapper = new BeanWrapper(this, object);
  }
}
  // MetaObject 的構造方法是 private 修改的,只能通過 forObj ect ()這個靜態方法創建 MetaObject 對象 
  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    if (object == null) {
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
  }

三、小結

MyBatis 根據自身需求,對反射 API 做了近一步封裝。其目的是簡化反射操作,爲對象字段的讀取和賦值提供更好的性能。

  • org.apache.ibatis.reflection.Reflector:緩存類的字段名和 getter/setter 方法的元信息,使得反射時有更好的性能。
  • org.apache.ibatis.reflection.invoker.Invoker::用於抽象設置和讀取字段值的操作。
  • org.apache.ibatis.reflection.TypeParameterResolver:針對 Java-Type 體系的多種實現,解析指定類中的字段、方法返回值或方法參數的類型。
  • org.apache.ibatis.reflection.DefaultReflectorFactory:默認的 Reflector 創建工廠。
  • org.apache.ibatis.reflection.factory.ObjectFactory:MyBatis 對象創建工廠,其默認實現 DefaultObjectFactory 通過構造器反射創建對象。
  • org.apache.ibatis.reflection.property:property 工具包,針對映射文件表達式進行解析和 Java 對象的反射賦值。
  • org.apache.ibatis.reflection.MetaClass:依賴 PropertyTokenizer 和 Reflector 查找表達式是否可以匹配 Java 對象中的字段,以及對應字段是否有 getter/setter 方法。
  • org.apache.ibatis.reflection.MetaObject:對原始對象進行封裝,將對象操作委託給 ObjectWrapper 處理。
  • org.apache.ibatis.reflection.wrapper.ObjectWrapper:對象包裝類,封裝對象的讀取和賦值等操作。

四、使用實例

 

ReflectorFactory reflectorFactory = new DefaultReflectorFactory();

		//使用Reflector讀取類元信息
		Reflector findForClass = reflectorFactory.findForClass(TUser.class);
		Constructor<?> defaultConstructor = findForClass.getDefaultConstructor();
		String[] getablePropertyNames = findForClass.getGetablePropertyNames();
		String[] setablePropertyNames = findForClass.getSetablePropertyNames();
		System.out.println(defaultConstructor.getName());
		System.out.println(Arrays.toString(getablePropertyNames));
		System.out.println(Arrays.toString(setablePropertyNames));
		//反射工具類初始化
		ObjectFactory objectFactory = new DefaultObjectFactory();
		TUser user = objectFactory.create(TUser.class);
		ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
		MetaObject metaObject = MetaObject.forObject(user, objectFactory, objectWrapperFactory, reflectorFactory);
		ObjectWrapper wrapperForUser = new BeanWrapper(metaObject, user);
		String[] getterNames = wrapperForUser.getGetterNames();
		String[] setterNames = wrapperForUser.getSetterNames();
		System.out.println(Arrays.toString(getterNames));
		System.out.println(Arrays.toString(setterNames));

		PropertyTokenizer prop = new PropertyTokenizer("userName");
		wrapperForUser.set(prop, "lison");
		System.out.println(user);
		
		//模擬數據庫行數據轉化成對象
		//1.模擬從數據庫讀取數據
		Map<String, Object> dbResult = new HashMap<>();
		dbResult.put("id", 1);
		dbResult.put("user_name", "lison");
		dbResult.put("real_name", "李曉宇");
		TPosition tp = new TPosition();
		tp.setId(1);
		dbResult.put("position_id", tp);
		//2.模擬映射關係
		Map<String, String> mapper = new HashMap<String, String>();
		mapper.put("id", "id");
		mapper.put("userName", "user_name");
		mapper.put("realName", "real_name");
		mapper.put("position", "position_id");

		//3.使用反射工具類將行數據轉換成pojo
		BeanWrapper objectWrapper = (BeanWrapper) metaObject.getObjectWrapper();
		Set<Entry<String, String>> entrySet = mapper.entrySet();
		for (Entry<String, String> colInfo : entrySet) {
			String propName = colInfo.getKey();
			Object propValue = dbResult.get(colInfo.getValue());
			PropertyTokenizer proTokenizer = new PropertyTokenizer(propName);
			objectWrapper.set(proTokenizer, propValue);
		}
		System.out.println(metaObject.getOriginalObject());
		
	}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章