反射工具箱「基础支持层」-MyBatis

MyBatis在进行参数处理、结果映射等操作的时候,会涉及大量的反射操作。java原生反射操作有些复杂且容易出错,MyBatis中提供了反射相关的工具类放在com.apache.ibatis.reflection包中。同时MyBatis中提供的测试用例也是非常值得一看的。
在这里插入图片描述

1.Reflector&ReflectorFactory

对JavaBean的定义:属性是通过getter/setter方法得到的,也就是说有属性A就会有getA(),setA(A)方法。
Reflector是反射的基础,里面缓存了反射操作需要使用的类的元信息。以下为各个字段的含义

public class Reflector {
  // 对应class的类型
  private final Class<?> type;
  // 可读属性的集合(存在相应getter方法的属性)
  private final String[] readablePropertyNames;
  // 可写属性的集合(存在相应setter方法的属性)
  private final String[] writablePropertyNames;
  // 记录属性对应的setter方法
  private final Map<String, Invoker> setMethods = new HashMap<>();
 // 记录属性对应的getter方法
  private final Map<String, Invoker> getMethods = new HashMap<>();
 // 记录setter方法的参数类型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
 // 记录getter方法返回值的类型
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  // 记录默认构造方法
  private Constructor<?> defaultConstructor;
  // 记录所有属性名称的集合 key为大写的属性,包含父类的属性
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
  // 构造器主要用于初始化并填充上面的属性
  public Reflector(Class<?> clazz) {
    type = clazz;
    addDefaultConstructor(clazz);
    // 填充Getter方法
    addGetMethods(clazz);
    // 填充Setter方法
    addSetMethods(clazz);
    // 处理没有Getter/Setter方法的字段
    addFields(clazz);
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

构造器中有一个重要的方法是addGetMethods(clazz);用于获取属性的getter方法。

  private void addGetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    Method[] methods = getClassMethods(clazz);
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveGetterConflicts(conflictingGetters);
  }

主要包含有3个步骤

1.getClassMethods(clazz);获取当前类所有的方法(包括父类)

  private Method[] getClassMethods(Class<?> clazz) {
    // 记录指定类中定义的全部方法和唯一签名以及对应的Method对象
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    while (currentClass != null && currentClass != Object.class) {
      // 记录当前类的全部方法
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
      Class<?>[] interfaces = currentClass.getInterfaces();
      for (Class<?> anInterface : interfaces) {
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
      }
	  // 获取父类继续循环
      currentClass = currentClass.getSuperclass();
    }

    Collection<Method> methods = uniqueMethods.values();
    // 转换成Methods数组返回
    return methods.toArray(new Method[0]);
  }

addUniqueMethods()方法会为每个方法生成唯一签名,并记录到uniqueMethods集合中:

  private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
      if (!currentMethod.isBridge()) {
        // 方法签名(全局唯一)格式为:返回值类型#方法名称:参数列表 
        String signature = getSignature(currentMethod);
        // 检查是否在子类中添加了该方法,如果添加了表示已经覆盖了,无需再次添加
        if (!uniqueMethods.containsKey(signature)) {
          uniqueMethods.put(signature, currentMethod);
        }
      }
    }
  }

2.然后从getClassMethos()方法返回的Method数组中查找该类中定义的getter方法,将其记录到conflictingGetters集合中,key为属性名称,value是该属性对应的getter方法集合。

3.当子类覆盖了父类的getter方法且返回值发生变化时,在步骤1中就会产生两个不同的方法,在java中这种操作是合法的但这又不是我们想要的,所以第3要使用addGetMethods(clazz);方法对这种覆盖的情况进行处理,同时会将处理得到的Getter方法记录到getMethods集合,并将其返回值类型填充到getTypes集合中。

  private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
      Method winner = null;
      String propName = entry.getKey();
      boolean isAmbiguous = false;
      for (Method candidate : entry.getValue()) {
        if (winner == null) {
          winner = candidate;
          continue;
        }
        Class<?> winnerType = winner.getReturnType();
        Class<?> candidateType = candidate.getReturnType();
        if (candidateType.equals(winnerType)) {
          if (!boolean.class.equals(candidateType)) {
            isAmbiguous = true;
            break;
          } else if (candidate.getName().startsWith("is")) {
            winner = candidate;
          }
        } else if (candidateType.isAssignableFrom(winnerType)) {
          // OK getter type is descendant
        } else if (winnerType.isAssignableFrom(candidateType)) {
          winner = candidate;
        } else {
          isAmbiguous = true;
          break;
        }
      }
      addGetMethod(propName, winner, isAmbiguous);
    }
  }

Reflector中提供了多个get*()方法用于读取记录的元信息。值得注意的是,在add*Method()获取Method时会统一封装为Invoker对象。

public interface Invoker {
  // 调用获取指定字段的值或执行指定的方法
  Object invoke(Object target, Object[] args) throws IllegalAccessException,     InvocationTargetException;
  // 属性的类型
  Class<?> getType();
}

Invoker的实现类有SetFieldInvoker、GetFieldInvoker、MethodInvoker。前两者通过field字段封装了对应的Field对象,两者的invoke()方法通过调用Field.get()/set()方法实现的。MethodInvoker通过代哦用Method.invoker()方法实现。
在这里插入图片描述
在这里插入图片描述
ReflectionFactory接口主要实现对Reflection对象的创建和缓存,该接口定义:

/**
 * 主要实现对Reflection对象的创建和缓存
 *
 * @author Clinton Begin
 */
public interface ReflectorFactory {
    /**
     * 监测ReflectorFactory对象是否会缓存
     *
     * @return
     */
    boolean isClassCacheEnabled();

    /**
     * 设置是否缓存ReflectorFactory对象
     *
     * @param classCacheEnabled
     */
    void setClassCacheEnabled(boolean classCacheEnabled);

    /**
     * 创建指定class对应的Reflector对象
     *
     * @param type
     * @return
     */
    Reflector findForClass(Class<?> type);

ReflectionFactory对象只有一个实现类DefaultReflectorFactory

2.TypeParameterResolver

它提供了一系列方法来解析指定类中的字段、方法返回值、或方法参数的类型。
首先复习一下java中的Type,Type接口源于原生JDK,它有4个子接口(ParameterizedType、GenericArrayType、TypeVariable、WildcardType)和1个实现类(Class)。

  • Class:它表示原始类型,Class类对象表示JVM中的一个类或接口,每个java类在JVM里都表现为一个Class对象
  • ParamterizedType:表示的是参数化类型,例如List、Map<Integer, String>、Service这种带有泛型的类型。
    Type getRawType(): 返回参数化类型中的原始类型,例如List的原始类型为List
    Type[] getActualArguments():获取参数化类型变量或实际类型列表,例如Map<Integer,String>的实际泛型列表是Integer、String
    Type getOwnerType():返回类型所属的类型,例如存在A类,其中定义了内部InnerA,则Inner<A,I>所属的类型为A,如果没顶层类型则返回为null
  • TypeVariable:表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如List中的T就是类型变量,它在编译时需要每转换为一个具体的类型后才能正常使用。
    Type[] getBounds():获取类型变量的上边界,如果未明确声明上边界则默认为Object,例如class Test中K的上界就是Person。
    D getGenericDeclaration():获取类型变量的原始类型 Test的原始类型就是Test
    String getName():获取在源码中定义的名字,上例中为K
    GenericArrayType() 表示的是数组类型且组成元素是ParameterizedType或者TypeVariable,例如List,该接口只有Type getGenericComponentType()一个方法,它返回数组的组成元素。
  • WildcardType表示的是通配符泛型,例如: ?extends Person
    Type[] getUpperBounds()返回泛型变量的上界
    Type[] getLowerBounds()返回泛型变量的下界

TypeParameterResolver中通过resolverFieldType()方法、resolverReturnType()、resolverParamType()方法分别解析字段类型、方法返回类型、方法参数类型。这三个方法都有用到resolveType()方法。在MyBatis此类的测试用例也非常丰富,可以看测试用例了解这个类的使用

3.ObjectFactory

该接口提供了多个create()方法的重载,通过这些create()方法可以创建指定类型的对象。

/**
 * MyBatis uses an ObjectFactory to create all needed new Objects.
 * 使用create创建指定类型的对象
 *
 * @author Clinton Begin
 */
public interface ObjectFactory {

    /**
     * 设置配置行
     *
     * @param properties configuration properties
     */
    default void setProperties(Properties properties) {
    }

    /**
     * 通过无参构造器创建指定的类对象
     *
     * @param <T>  the generic type
     * @param type Object type
     * @return the t
     */
    <T> T create(Class<T> type);

    /**
     * 根据参数列表选择合适的构造器创建对象
     *
     * @param <T>                 the generic type
     * @param type                Object type
     * @param constructorArgTypes Constructor argument types
     * @param constructorArgs     Constructor argument values
     * @return the t
     */
    <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

    /**
     * 监测指定类型是否为集合
     *
     * @param <T>  the generic type
     * @param type Object type
     * @return whether it is a collection or not
     * @since 3.1.0
     */
    <T> boolean isCollection(Class<T> type);

}

DefaultObjectFacroty是MyBatis提供的ObjectFactory接口的唯一实现,它是一个反射工厂,其create()方法通过调用instantiateClass()方法实现。

4.Property工具集

包含三个属性工具:PropertyTokenizer、PropertyName和PropertyCopier。
PropertyTokenizer:解析类似“orders[0].items[0].name”的表达式,它可以迭代处理嵌套多层表达式。next()方法 就会创建一个新的PropertyTokenizer对象。
PropertyNamer是另一个工具类。

public final class PropertyNamer {

    private PropertyNamer() {
      
    }

    /**
     * 将getter、setter方法名转为属性名(去掉前缀is、get、set)
     *
     * @param name
     * @return
     */
    public static String methodToProperty(String name) {
        if (name.startsWith("is")) {
            name = name.substring(2);
        } else if (name.startsWith("get") || name.startsWith("set")) {
            name = name.substring(3);
        } else {
            throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
        }
        if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
        }
        return name;
    }

    /**
     * 检测方法名是否对应属性名
     *
     * @param name
     * @return
     */
    public static boolean isProperty(String name) {
        return isGetter(name) || isSetter(name);
    }

    /**
     * 检查是否为getter方法
     *
     * @param name
     * @return
     */
    public static boolean isGetter(String name) {
        return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
    }
    /**
     * 检查是否为setter方法
     *
     * @param name
     * @return
     */
    public static boolean isSetter(String name) {
        return name.startsWith("set") && name.length() > 3;
    }

}

PropertyCopier是一个属性拷贝工具

public final class PropertyCopier {

  private PropertyCopier() {
    // Prevent Instantiation of Static Class
  }

  public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
    Class<?> parent = type;
    while (parent != null) {
      final Field[] fields = parent.getDeclaredFields();
      for (Field field : fields) {
        try {
          try {
            field.set(destinationBean, field.get(sourceBean));
          } catch (IllegalAccessException e) {
            if (Reflector.canControlMemberAccessible()) {
              field.setAccessible(true);
              field.set(destinationBean, field.get(sourceBean));
            } else {
              throw e;
            }
          }
        } catch (Exception e) {
          // Nothing useful to do, will only fail on final fields, which will be ignored.
        }
      }
      parent = parent.getSuperclass();
    }
  }

5.MetaClass

通过Reflector和PropertyTokenizer组合使用,可以解析复杂的属性表达式。是对类级别元信息的封装

public class MetaClass {
    /**
     * 主要用于缓存Reflector对象
     */
    private final ReflectorFactory reflectorFactory;
    /**
     * 创建MetaClass时会指定该类,该对象记录了该类相关的元信息
     */
    private final Reflector reflector;

    private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
        this.reflectorFactory = reflectorFactory;
        this.reflector = reflectorFactory.findForClass(type);
    }

    public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
        return new MetaClass(type, reflectorFactory);
    }
}

MetaClass可以获取类的元信息

  @Test
  void shouldTestDataTypeOfGenericMethod() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(GenericConcrete.class, reflectorFactory);
    // 根据属性名字,获取属性的类型
    assertEquals(Long.class, meta.getGetterType("id"));
    assertEquals(Long.class, meta.getSetterType("id"));
  }

findProperty()方法可以查看到是否有某属性

@Test
void shouldCheckGetterExistance() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    assertTrue(meta.hasGetter("richField"));
    assertEquals("richType.richProperty", meta.findProperty("richType.richProperty", false));
}

hasSetter()、hasGetter判断是否有对应的属性

    /**
     * 判断是否有对应的属性,最终会调用Reflector.addFields()方法,当字段没有对应的getter/setter会自动添加
     *
     * @param name
     * @return
     */
    public boolean hasGetter(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            // 判断指定的属性是否有getter方法
            if (reflector.hasGetter(prop.getName())) {
                MetaClass metaProp = metaClassForProperty(prop);
                return metaProp.hasGetter(prop.getChildren());
            } else {
                return false;
            }
        } else {
            return reflector.hasGetter(prop.getName());
        }
    }

6.ObjectWrapper

/**
 * 对对象的封装
 *
 * @author Clinton Begin
 */
public interface ObjectWrapper {

    /**
     * 普通对像,就会调用getter方法。如果是集合类则获取指定key或者下标对应的value
     *
     * @param prop
     * @return
     */
    Object get(PropertyTokenizer prop);

    /**
     * 普通bean调用setter方法,集合类设置指定key或下标对应的value值
     *
     * @param prop
     * @param value
     */
    void set(PropertyTokenizer prop, Object value);

    /**
     * 查找属性表达式指定的属性
     *
     * @param name
     * @param useCamelCaseMapping 是否忽略表达式中的下划线
     * @return
     */
    String findProperty(String name, boolean useCamelCaseMapping);

    /**
     * 查找可写属性的名称集合
     *
     * @return
     */
    String[] getGetterNames();

    /**
     * 查找可读属性的名称集合
     *
     * @return
     */
    String[] getSetterNames();

    /**
     * 获取属性表达式属性的setter方法的参数类型
     *
     * @param name
     * @return
     */
    Class<?> getSetterType(String name);

    /**
     * 属性表达式对应属性的getter方法返回值的类型
     *
     * @param name
     * @return
     */
    Class<?> getGetterType(String name);

    /**
     * 是否有setter方法
     *
     * @param name
     * @return
     */
    boolean hasSetter(String name);

    /**
     * 是否有getter方法
     *
     * @param name
     * @return
     */
    boolean hasGetter(String name);

    /**
     * 为属性表达式指定的属性创建相应的MetaClass对象
     *
     * @param name
     * @param prop
     * @param objectFactory
     * @return
     */
    MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);

    /**
     * 封装的对象是否为集合
     *
     * @return
     */
    boolean isCollection();

    /**
     * 调用集合中的add()方法
     *
     * @param element
     */
    void add(Object element);

    /**
     * 调用集合中的addAll()
     *
     * @param element
     * @param <E>
     */
    <E> void addAll(List<E> element);

}

BaseWrapper实现类ObjectWrapper接口的抽象类,其中setCollectionValue()和getCollectonValue().方法会解析属性表达式的索引信息,然后获取/设置对应项
BeanWrapper继承了Basewrapper

7.MetaObject

public class MetaObject {

    /**
     * 原始JavaBean对象
     */
    private final Object originalObject;
    private final ObjectWrapper objectWrapper;
    private final ObjectFactory objectFactory;
    private final ObjectWrapperFactory objectWrapperFactory;
    private final ReflectorFactory reflectorFactory;
    ......
}

构造方法会根据传入的原始对象的类型以及ObjectFactory工厂的实现,创建相应的ObjectWrapper对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值