mybatis-reflection反射包解析

news/2024/5/19 3:31:11 标签: java, mybatis, 反射, 后端

反射包解析

概述

mybatis反射包,提供了反射相关的工具,为啥要提供这些工具呢,java不是已经提供反射的功能了吗?

java反射虽然已经很强大,但是api偏底层,要想使用好可能会需要大量重复代码,可能使用不当也会产生性能问题,因此mybatis提供反射包提炼、封装、增强反射api,让上层应用用的更舒心。

包内容:

在这里插入图片描述

接下来会挑重点的类讲解下原理。

Invoker

invoker单独有一个子包,概念比较独立,也比较简单,代表执行器。包装了Method、Field的执行

在这里插入图片描述

Invoker定义:

java">public interface Invoker {
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  Class<?> getType();
}

主要定义了一个invoke方法,和Method.invoke几乎一样,这里做了抽象主要是把Field给包了进来。

GetFieldInvoker的invoke实现为 field.get(target,arg)

SetFieldInvoker的invoke实现为 field.set(target,arg)

factory

这里的工厂子包只包含了ObjectFactory和其实现类DefaultObjectFactory。

java">public interface ObjectFactory {

  default void setProperties(Properties properties) {
    // NOP
  }
  <T> T create(Class<T> type);
  <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
  <T> boolean isCollection(Class<T> type);

}

就是通用的对象创建工厂,DefaultObjectFactory就是用构造函数去实例化对象,比较简单。

property

这个子包主要提供对类属性的反射解析工具。重点是PropertyTokenizer,意思是属性分隔解析器。

解析格式例如:order[0].item[name].firstName这样的表达式,有点类似json表达式,mybatis通过此类提供了表达式解析的能力,在处理嵌套属性时更加方便和通用。

java">public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
  /**
   * 当前属性
   */
  private String name;

  /**
   * 当前完整分词
   */
  private final String indexedName;

  /**
   * 索引值
   * 如果是数组[0],则是0
   * 如果是map[key],则是key、
   */
  private String index;

  /**
   * 剩余字符串
   */
  private final String children;

  /**
   *  构造函数内部就完成解析
   */
  public PropertyTokenizer(String fullname) {
    int delim = fullname.indexOf('.');
    if (delim > -1) {
      name = fullname.substring(0, delim);
      children = fullname.substring(delim + 1);
    } else {
      name = fullname;
      children = null;
    }
    indexedName = name;
    delim = name.indexOf('[');
    if (delim > -1) {
      index = name.substring(delim + 1, name.length() - 1);
      name = name.substring(0, delim);
    }
  }

  @Override
  public boolean hasNext() {
    return children != null;
  }

  @Override
  public PropertyTokenizer next() {
    return new PropertyTokenizer(children);
  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
  }

PropertyTokenizer使用了迭代器模式实现了递归解析子串,挺巧妙,之后有类似递归的需求可以借鉴一下,代码简单清晰。

数据:order[0].item[name].firstName

解析结果:

name=orde

indexedName=order[0]

index=0

children=item[name].firstName

结果简单清晰明了,有相关场景可以直接使用。

Reflector

Reflector反射器,保存类反射元数据

java">public class Reflector {
  /**
   * 对应的类
   */
  private final Class<?> type;
  /**
   * 可读的属性名集合
   */
  private final String[] readablePropertyNames;
  /**
   * 可写的属性名集合
   */
  private final String[] writablePropertyNames;
  /**
   * 所有set方法,
   */
  private final Map<String, Invoker> setMethods = new HashMap<>();
  /**
   * 所有get方法
   */
  private final Map<String, Invoker> getMethods = new HashMap<>();
  /**
   * set方法的入参类型
   */
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  /**
   * get方法的返回类型
   */
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  /**
   * 默认构造函数
   */
  private Constructor<?> defaultConstructor;
  /**
   * 不区分大小写的属性集合
   * key 属性名全大写, value 属性名
   */
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

相关属性都是在构造函数中通过Class对象加工获取的,基本包含了操作类的元数据。额外提供了不区分大小写的属性集合,为了属性查找、下划线转驼峰提供支持。

总结就是Class类增强,提供对property的一些简便操作方法。

ReflectorFactory

Reflector工厂,就是控制Reflector的创建,目的是为了缓存Reflector,Reflector虽好用,但是其创建过程比较重,使用了java反射api,如果对同一class频繁创建性能会被影响,且反射元数据也不会更改,缓存其是必须的。

DefaultReflectorFactory直接使用HashMap缓存Reflector,key是Class,value是Reflector。

MetaClass

MetaClass又是Reflector的增强类,包含了Reflector和ReflectorFactory,又额外提供了对属性表达式的解析。

java">public class MetaClass {
  private final ReflectorFactory reflectorFactory;
  private final Reflector reflector;
}

上层应用使用MetaClass操作类,MetaClass内部使用ReflectorFactory缓存。

提供一些property相关的方法,实现委托给Reflector完成。

在这里插入图片描述

涉及属性表达式解析则交给PropertyTokenizer。

Wapper

wapper包,放着ObjectWapper及其实现类。对象包装器,在一个具体对象实例上添加反射功能,上面的Reflector、MetaClass都是在对静态Class各种操作,ObjectWrapper是真正有个具体的对象实例,可以执行反射操作了。

java">public interface ObjectWrapper {
  Object get(PropertyTokenizer prop);
  void set(PropertyTokenizer prop, Object value);
  String findProperty(String name, boolean useCamelCaseMapping);
  String[] getGetterNames();
  String[] getSetterNames();
  Class<?> getSetterType(String name);
  Class<?> getGetterType(String name);
  boolean hasSetter(String name);
  boolean hasGetter(String name);
  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
  boolean isCollection();
  void add(Object element);
  <E> void addAll(List<E> element);

单看提供的方法比较简单,属性动态设置/获取,属性查找、getter/setter方法操作。

在这里插入图片描述

BaseWrapper

在这里插入图片描述

提供了集合的通用方法,属性转换集合、获取/设置集合中的值。

BeanWrapper

普通Bean包装器

java">public class BeanWrapper extends BaseWrapper {

  private final Object object;
  private final MetaClass metaClass;
}

具体对象object、MetaClass包装,MetaClass的具象化。

借助MetaClass实现ObjectWapper定义的方法,比较简单。

java">@Override
public Object get(PropertyTokenizer prop) {
  // index不为空代表是集合类型
  if (prop.getIndex() != null) {
    // 解析集合的值
    Object collection = resolveCollection(prop, object);
    return getCollectionValue(prop, collection);
  } else {
    return getBeanProperty(prop, object);
  }
}

get和set方法都有对集合类型的处理。

MetaObject

对象元数据,对ObjectWapper的进一步增强,表达式解析反射完全支持。

最主要两个方法getValue、setValue

java">public Object getValue(String name) {
  PropertyTokenizer prop = new PropertyTokenizer(name);
  // 如果有子串递归解析
  if (prop.hasNext()) {
    // 获取当前层值的MetaObject
    MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
    if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
      return null;
    } else {
      // 递归解析子串的值
      return metaValue.getValue(prop.getChildren());
    }
  } else {
    return objectWrapper.get(prop);
  }
}
java">public void setValue(String name, Object value) {
  PropertyTokenizer prop = new PropertyTokenizer(name);
  if (prop.hasNext()) {
    MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
    if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
      if (value == null) {
        // don't instantiate child path if value is null
        return;
      } else {
        metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
      }
    }
    metaValue.setValue(prop.getChildren(), value);
  } else {
    objectWrapper.set(prop, value);
  }
}

在复杂对象时可以方便的通过表达式,反射设置、获取值。

总结

反射包重点在于Reflector、MetaClass、ObjectWapper、MetaObject的设计,职责清晰一层一层加强,遵守单一原则,在我们日常业务实现设计时值得借鉴。


http://www.niftyadmin.cn/n/1182380.html

相关文章

eclipse常用知识

选中你所要查看的方法名&#xff0c;ctrlshiftG就可以查看所有调用过该方法的地方了。在Search视图里面可以查看得到 这个样子是可以的&#xff0c;你也可以按CtrlH全文检索一下 eclipse 折叠快捷键 ctrl shift / 展开 ctrl shift *转载于:https://www.cnblogs.com/sfshine…

码农何去何从

转自&#xff1a;http://www.cnblogs.com/unruledboy/archive/2012/09/20/OldCoder.html 对大龄码农来说&#xff0c;出路主要有以下几条&#xff1a; 继续开发生涯&#xff0c;做资深码农&#xff0c;从senior、team lead、tech lead到principal&#xff0c;如果你无欲无求&am…

mybatis-cache缓存包解析

概述 缓存包是mybatis对缓存的抽象和实现&#xff0c;可为其他模块提供缓存的支持。其设计也很值得借鉴。 大概分为 缓存定义&#xff08;抽象、异常、缓存键&#xff09;缓存实现缓存装饰者 可以重点在于缓存策略装饰模式实现&#xff0c;可以复习复习装饰模式啦。 Cache …

关闭mysql报警音

方法一. 临时性关闭 例&#xff1a;加入参数 -mysql -uroot -padmin --no-deep 如果启动不加入参数 --no-deep 报警声依旧存在。 方法二.永久性关闭 找到mysql安装目录下的 mysql.ini 文件 在[mysql] 语句后面添加一条 no-beep PS: 如果想把电脑默认的报警音全部关掉操作如下…

mybatis-type 类型包解析

概述 类型包是mybatis提供类型转换处理的包&#xff0c;mybatis提供了许多基本类型处理器的实现。在javaType和jdbcType之间转换时所用到。 TypeHandler 本包的重点类&#xff0c;类型处理器 public interface TypeHandler<T> {// 往Statement中设置参数&#xff0c;…

Wireshark使用及tcp三次握手

本文转载自http://www.cnblogs.com/TankXiao/archive/2012/10/10/2711777.html 记得大学的时候就学习过TCP的三次握手协议&#xff0c;那时候只是知道&#xff0c;虽然在书上看过很多TCP和UDP的资料&#xff0c;但是从来没有真正见过这些数据包&#xff0c; 老是感觉在云上飘一…

JAVA虚拟机加载类的方式解析

2019独角兽企业重金招聘Python工程师标准>>> 虚拟机加载类的途径 1、由 new 关键字创建一个类的实例 在由运行时刻用 new 方法载入 如&#xff1a;Dog dog &#xff1d; new Dog&#xff08;&#xff09;&#xff1b; 2、调用 Class.forName() 方法 通过反射加载类型…

mybatis-binding 绑定包解析

概述 为了实现直接调用Mapper接口类的方法&#xff0c;便达到调用sql的目标&#xff0c;mybatis-binding包提供了Mapper接口的代理类和其方法的代理类。主要起到连接 Mapper.java 和 Mapper.xml的作用。 方法映射绑定 为了连接Mapper接口的方法 和 Mapper.xml的statement&am…