一、Mybatis源码-反射模块

1、JavaBean规范

JavaBean 具有如下特征:

  1. 所有的属性都是私有的(通过 getter 和 setter 访问)
  2. 拥有公有的无参构造函数
  3. 提供 setter/getter
  4. 实现 Serializable 接口

2、Reflector 和 ReflectorFactory

出于性能方面的考虑,Mybatis 不是等到使用的时候去解析 XML/反射类,而是为每一个类提供了反射器类 Reflector ,该类中存储了反射需要使用的类的元信息

2.1 Reflector 属性

2.1.1 属性

  private static final String[] EMPTY_STRING_ARRAY = new String[0];

  private Class<?> type;
  //可读属性的名称集合,可读属性就是存在相应 getter 方法的属性,初始值为空数组
  private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
  //可写属性的名称集合,可写属性就是存在相应 setter 方法的属性,初始值为空数组
  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
  //记录了属性相应的 setter 方法,key 是属性名称,value 是 Invoker 对象,它是对 setter 方法对应 Method 对象的封装
  private Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
  //记录了属性相应的 getter 方法,key 是属性名称,value 是 Invoker 对象,它是对 getter 方法对应 Method 对象的封装
  private Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
  //记录了属性相应的 setter 方法的参数值类型,key 是属性名称,value 是 setter 方法的参数类型
  private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
  //记录了属性相应的 getter 方法的参数值类型,key 是属性名称,value 是 getter 方法的返回值类型
  private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
  //记录了默认构造方法
  private Constructor<?> defaultConstructor;

  //记录了所有属性名称的集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();

从上面类的属性中,我们可以看出:

  1. 一个反射器 Reflector 对应着一个 Class 对象
  2. 记录了默认构造函数
  3. 其余的是属性及其 setter/gettter 相关
    在这里插入图片描述
    对于一个属性(只有有 setter/getter 才能被称之为属性)
    (1)如果是可读的(有 getter 方法),则 Reflector 会将其及其方法处理后放入对应的可读相关的集合中;
    (2)如果是可写的(有 setter 方法),则 Reflector 会将其及其方法处理后放入对应的可写相关的集合中;
    (3)最后使用 Map<String, String> caseInsensitivePropertyMap 记录所有的属性。

2.1.2 Invoker 接口

在存储方法的时候,Reflector 使用的是 Map<String, Invoker>,而不是 Map<String, Method>。该接口定义了方法的调用和获取类型,定义如下:

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

在这里插入图片描述

  • MethodInvoker:方法的 Invoker
  • GetFieldInvoker:如果没有 getter,则使用该方法,通过 Filed 类直接读取成员变量的值
  • SetFieldInvoker:如果没有 setter,则使用该方法,通过 Filed 类直接设置成员变量的值

通过该封装以后,本来需要声明 Map<String, Method>Map<String, Field> 表示的,只需要使用 Map<String, Field> 即可表示

2.2 Reflector 对外提供的方法

Reflector 对外提供的方法主要与构造函数和属性相关。
在这里插入图片描述

  • 构造函数:根据 Class 对象,设置 Reflector 相应的成员变量。
public Reflector(Class<?> clazz)
  • 查找是否有相应的属性
public String findPropertyName(String name)
  • 判断是否有默认构造函数
public boolean hasDefaultConstructor()
  • 获取默认的构造函数
public Constructor<?> getDefaultConstructor()
  • 获取 Reflector 对应的 Class 类型
public Class<?> getType()
  • getter 相关的方法
//获取所有的可读属性
public String[] getGetablePropertyNames()
//获取所有的可读属性的 Invoker
public Invoker getGetInvoker(String propertyName)
//获取对应属性的类型
public Class<?> getGetterType(String propertyName)
//对应属性是否有相应的 getter
public boolean hasGetter(String propertyName)
  • setter相关的方法
//获取所有的可写属性
public String[] getSetablePropertyNames()
//获取所有的可写属性的 Invoker
public Invoker getSetInvoker(String propertyName)
//获取可写属性 setter 方法的参数 
public Class<?> getSetterType(String propertyName)
//对应属性是否有相应的 setter
public boolean hasSetter(String propertyName)

2.3 Reflector 私有方法

每个 Reflector 对应缓存一个类的元反射信息,通过 Map 进行缓存,后续通过键查找即可。因此,就涉及到几个方法:

2.3.1获取方法签名

根据函数名称、参数和返回值类型来取得签名,保证方法的唯一性

private String getSignature(Method method)

该方法获取方法的签名,签名的格式为:

返回值类型#方法名:参数1,参数2,参数3

签名的目的是为了保证唯一性,那么使用语言本身的特性来保证唯一性是最好的:

  1. 方法名不一致,则方法不一致
  2. 返回值不一致或者不是其子类,则方法不一致
  3. 参数数量,参数类型顺序不一致,则方法不一致
    因此,以上的规则保证方法的唯一性。

2.3.2获取类的所有方法

private Method[] getClassMethods(Class<?> cls)

注意,由于在获取方法时,通过调用当前类及其除 Object 之外的所有父类的 getDeclaredMethods()getInterfaces() 方法,因此,其获取到的方法是该类及其父类的所有方法。
由此,产生一个问题,如果子类重写了父类中的方法,如果返回值相同,则可以通过键值重复来去掉。但是,如果方法返回值类型是相同实体方法返回值类型的子类型,则就会导致两个方法是同一个方法,但是签名不同。因此,需要解决此类冲突。

2.3.3解决方法冲突:getter方法冲突解决

private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters)

到了此步骤,属性已经以 propName->List<Method> 的形式存在于内存中。此时,需要将 Map<String, List> 转换为 Map<String, Invoker>
该方法只需要特别注意当返回值类型不同,则哪一个方法的返回值是另一个方法返回值类型的子类型,就把 propName 指向该方法包装成的 Invoker。这是因为重写(override)时,重写方法的返回值类型可以是被重写方法的子类。

2.3.4解决方法冲突:setter方法冲突解决

首先需要特别注意的是,为什么 setter 也会出现冲突,毕竟没有返回值类型。主要是因为泛型的存在:

public void setPrice(T price);
public void setPrice(Double price);
public void setPrice(Integer price);

显然,遇到此类情况,子类中的方法才是我们需要的:

    Class<?> paramType1 = setter1.getParameterTypes()[0];
    Class<?> paramType2 = setter2.getParameterTypes()[0];
    if (paramType1.isAssignableFrom(paramType2)) {
      return setter2;
    } else if (paramType2.isAssignableFrom(paramType1)) {
      return setter1;
    }

参数中,父类方法泛型经过类型擦除后,变成 Object 。因此,通过以上的方法,哪个是子类,我们就获取哪一个。

3、ReflectorFactory

看名称,工厂方法,是为了创建个缓存 Reflector 的:

/**
 * 主要负责 Reflector 对象的创建和缓存
 */
public interface ReflectorFactory {

  /**
   * 检测 ReflectorFactory 对象是否会缓存 Reflector 对象
   */
  boolean isClassCacheEnabled();
  /**
   * 设置是否缓存
   */
  void setClassCacheEnabled(boolean classCacheEnabled);
  /**
   * 缓存中查找 Class 对应的 Reflector 对象,找不到则创建
   */
  Reflector findForClass(Class<?> type);
}

只有三个方法:是否缓存,设置要不要缓存,根据类型查找 Reflector 对象,找不到则创建。其与 Reflector 的关系:
在这里插入图片描述
mybatis 为我们提供了该方法的默认实现 DefaultReflectorFactory 。该类的实现很简单,就是通过 ConcurrentMap<Class<?>, Reflector>Reflector 进行缓存。

4、MetaClass

Reflector 实现了实体类元信息的封装但是类中的成员变量是类的情况没有进行处理。而 MetaClass 通过 ReflectorFactory 类型的成员变量,实现了实体类中成员变量是类情况的处理,通过与属性工具类的结合,实现了对复杂表达式的解析和实现了获取指定描述信息的功能。

4.1 成员变量

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

MetaClass 有两个成员变量,分别是 reflectorFactoryreflector

4.2 创建

MetaClass 构造函数是私有的:

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

但是,其提供了两个创建的方法,这两个方法也是通过该方法进行创建对象的。

  • 通过 Class 对象进行了 Reflector 对象的创建,并赋值给成员变量
  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }
  • 通过属性进行创建
  /**
   * 通过属性名称,获取属性的 MetaClass
   */
  public MetaClass metaClassForProperty(String name) {
    Class<?> propType = reflector.getGetterType(name);
    return MetaClass.forClass(propType, reflectorFactory);
  }

4.3 方法

该类中最重要的方法是:

  /**
   * 解析复杂的属性表达式,例如:<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);
      }
    }
    //tele.name
    return builder;
  }

理解了这个方法(递归,该类中有很多类似的),就可以很好的对这个类进行理解,以查找(richType.richProperty)为例:

  1. 通过 PropertyTokenizer 对表达式进行解析, 得到当前的 name=richType, children=richProperty
  2. 从 reflector 中查找该 richType 属性
  3. 将 richType 添加到 builder 中
  4. 使用 metaClassForProperty 创建 richType 的 MetaClass
  5. 递归调用自身来处理子表达式

退出的条件就是没有子表达式。这个是为了,我们类中有成员变量是类,我们可以通过其找到他们的所有类及其属性。
注意,在此过程中,ReflectorFactory 一直是同一个,而其内部缓存了多个 Reflector 对象。

5、MetaObject

对象级别的元信息进行封装!

6、总结

上面类的关系:
在这里插入图片描述
Reflector 实现了实体类元信息的封装但是类中的成员变量是类的情况没有进行处理。而 MetaClass 通过 ReflectorFactory 类型的成员变量,实现了实体类中成员变量是类情况的处理,从而结合属性工具实现了对复杂表达式的处理。

发布了444 篇原创文章 · 获赞 113 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/panchang199266/article/details/102232592