MyBatis|反射模块

66 阅读5分钟

我正在参加「掘金·启航计划」 「反射基本 API 使用」juejin.cn/post/720144…

「反射实战:从 cotroller 类找到 xml 文件」juejin.cn/post/724667…

以前写过基本的反射使用,并且实操过。虽然只是小试牛刀,但已经感受到了反射的强大与魅力。MyBatis 是 Java 写的框架,自然少不了反射的大量运用,为了降低反射使用成本,MyBatis 封装了反射工具箱:位于 org.apache.ibatis.reflection 包中。下面是 MyBatis-3.5.10 的源码:

Reflector

Reflector 是 MyBatis 放射模块的基础,要使用反射模块操作一个 Class 都会将其先封装为一个 Reflector 对象。它会缓存反射操作需要的类的信息,比如:构造方法/属性名/get、set方法等。

初始化构造函数

addDefaultConstructor 会将传入的 class 的空构造函数缓存,初始化 defaultConstructor 字段

获得当前 class 所有方法

getClassMethods 获得 class 及其父类/接口(public)的方法缓存,其中处理的时候会用到桥接函数,判断不是桥接函数才会加入。

桥接方法

桥接方法:当一个子类在继承或者实现一个父类或接口的泛型方法时,在子类中明确指定了泛型类型,那么在编译时编译器会自动生成桥接方法。

什么是桥接?下面有两个类,AbstractEntity 实现了 Entity 的泛型方法,在字节码中 AbstractEntity 会生成四个方法,其中两个方法是编译器自动生成的与父类方法签名一致的桥接方法。下面的就是获取到 AbstractEntity 生成的方法,其中两个是和父类一致的桥接方法

interface Entity<T> {
  T getId();

  void setId(T id);
}
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;
  }
}

获取所有的 get/set 方法

addGetMethods/addSetMethods

都是先遍历出 get/set 相关的方法,像上面的方法,有泛型方法和具体的实现类,那么这里加入的时候会判断 isAssignableFrom「一个类是另一个类的子类或者相同类,那么前者就可以被赋值给后者」。

最后再加入 getMethods(基于 Method 的 Invoker类)、getTypes(方法类型)/setMethods、setTypes

加入属性

addFields 会遍历当前的属性以及父类的属性字段并加入(当然不会有实现的接口)

赋值可读/写属性数组

readablePropertyNames = getMethods.keySet().toArray(new String[0]);

writablePropertyNames = setMethods.keySet().toArray(new String[0]);

最后将所有属性转为大写保存到 caseInsensitivePropertyMap 属性中。

  public Reflector(Class<?> clazz) {
    type = clazz;
    addDefaultConstructor(clazz);
    Method[] classMethods = getClassMethods(clazz);
    if (isRecord(type)) {
      addRecordGetMethods(classMethods);
    } else {
      addGetMethods(classMethods);
      addSetMethods(classMethods);
      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);
    }
  }

ReflectorFactory

Reflector 工厂接口,用于创建和缓存 Reflector 对象

DefaultReflectorFactory

Reflector 工厂接口的具体实现类,其中 findClass 最重要

如果允许缓存就从缓存里面拿,没有则 new

不允许缓存就每次都 new

public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
        // synchronized (type) removed see issue #461
        return MapUtil.computeIfAbsent(reflectorMap, type, Reflector::new);
    } else {
        return new Reflector(type);
    }
}

Invoker

在 Reflector 对象的初始化过程中,所有属性的 getter/setter 方法都会被封装成 MethodInvoker 对象,没有 getter/setter 的字段也会生成对应的 Get/SetFieldInvoker 对象。

默认对象工厂

ObjectFactory 是 MyBatis 中的反射工厂,其中提供了两个 create() 方法的重载,我们可以通过两个 create() 方法创建指定类型的对象。

其中 DefaultObjectFactory 是默认实现,我们还可以在 mybatis-config.xml 中配置自定义 ObjectFactory 接口扩展实现类。

属性解析

在 refelection/property 包下的三个类:

PropertyTokenizer

负责解析由 “.”和“[]” 构成的表达式,因为继承了 Iterator 接口, 可以处理多层嵌套

PropertyCopier

属性拷贝的工具类,提供了 Spring 中 BeanUtils.copyProperties() 类似的功能,实现相同类型的两个对象之间的属性值拷贝。

PropertyNamer

转换方法名到属性名,以及检测一个方法是否为 getter / setter 方法。

MetaClass

MetaClass 只有下面两个属性,依赖 Reflector。它提供获取类中属性描述信息的功能。且其中也大量依赖了 PropertyTokenizer 解析表达式。

private final ReflectorFactory reflectorFactory;
private final Reflector reflector;

ObjectWrapper

MetaClass 中封装的是 Class 元信息,ObjectWrapper 封装的则是对象元信息。在 ObjectWrapper 中抽象了一个对象的属性信息,并提供了查询对象属性信息的相关方法,以及更新属性值的相关方法。

  • BaseWrapper 是抽象类,其其中方法都是针对集合对象的处理,其中都是依赖 PropertyTokenizer 解析类。
  • CollectionWrapper 是 ObjectWrapper 接口针对 Collection 集合的一个实现,其中封装了Collection 集合对象,只有 isCollection()、add()、addAll() 方法以及从 BaseWrapper 继承下来的方法是可用的,其他方法都会抛出 UnsupportedOperationException 异常
  • BeanWrapper 继承了 BaseWrapper 抽象类,底层除了封装了一个 JavaBean 对象之外,还封装了该 JavaBean 类型对应的 MetaClass 对象,以及从 BaseWrapper 继承下来的 MetaObject 对象
  • MapWrapper 继承了 BaseWrapper ,里面只有一个属性 Map<String, Object> 其中的相关操作都是 get/put

总结

总的来说学下来感觉代码量有点大,虽然以前学过基本的反射 API ,但真的就是基本的,比如 判断桥接方法和 isAssignableFrom 方法都是先学,就感觉有点吃力。到现在也是有点点懵。

上面介绍了反射工具箱中最核心、最底层的 Reflector 类的核心实现;接下来介绍了反射工具箱在 Reflector 基础之上提供的各种工具类,其中包括 ReflectorFactory 工厂类、ObjectFactory 工厂类、ObjectWrapper 包装类以及记录元数据的 MetaClass 等。

还需继续努力。