myBatis源码解析-反射篇(4)

1 property包-主要对类的属性进行操作的工具包

1.1 PropertyCopier包利用反射类Filed进行属性复制

复制代码

// 该类作用将sourceBean与destinationBean相同属性名的属性进行值复制

public class PropertyCopier {

  // 属性复制

  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 {

          field.setAccessible(true); // 设置该属性的访问权限(包括私有属性)

          field.set(destinationBean, field.get(sourceBean)); // 此处调用2个方法,filed.get(objectA)  获取objectA中的filed属性值. filed.set(objectB,value) 将value值赋值给ObjectB的filed属性

        } catch (Exception e) { // 异常直接忽略掉(对于非公共属性直接忽略)

          // Nothing useful to do, will only fail on final fields, which will be ignored.

        }

      }

      parent = parent.getSuperclass(); // 获取父类,循环复制父类属性

    }

  }

}

复制代码

该类主要功能是将sourceBean与destinationBean相同属性名的属性进行值复制,是一个属性工具类。

1.2 PropertyNamer根据方法名获取属性名称

复制代码

public class PropertyNamer {

  // 获取getxxx,isxxx,setxxx后的xxx属性

  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'.");

    }

    // 此处我们默认使用驼峰命名,如setName,那获取的属性名应为name而不是Name

    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;

  }

复制代码

该类主要作用是从set,is,get方法中获取属性,是一个属性工具类。

1.3 PropertyTokenizer解析属性集合,此处使用迭代器模式

复制代码

  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);

    }

  }

复制代码

此方法比较简单,举个例子。如我们要解析group[0].user[0].name这一串字符,那经过一次迭代获取如下结构

children = user[0].name

indexedName = group[0]

index = 0

name = group

使用迭代器方法hasNext()判断children是否为null,若不为null,则继续解析。此方法比较重要,在后文中对关于复杂属性的解析,都使用了此类,需要重要理解。

2. Invoker包分析 - 主要对反射类Filed,Method方法进行封装

2.1 执行器接口(将设置属性,获取属性,方法执行全都用Invoker进行封装,充分体现了面向接口编程)

复制代码

public interface Invoker {

  // 执行方法

  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  Class getType();

}

复制代码

提供对外通用接口,具体执行器需实现此接口。

2.2 获取对象属性的执行器

复制代码

public class GetFieldInvoker implements Invoker {


  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {

    return field.get(target); // 调用反射类Filed.get()方法,获取对象属性

  }

}

复制代码

GetFieldInvoker内部封装了Filed.get()方法获取对象属性。

2.3 设置对象属性执行器

复制代码

public class SetFieldInvoker implements Invoker {

  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {

    field.set(target, args[0]); // 调用Filed.set()方法,设置对象属性

    return null;

  }

}

复制代码

SetFieldInvoker内部封装了Filed.set()方法设置对象属性。

2.4 对象方法执行器

复制代码

public class MethodInvoker implements Invoker {

  private Class type;

  private Method method;

  public MethodInvoker(Method method) {

    this.method = method;

    if (method.getParameterTypes().length == 1) {

      type = method.getParameterTypes()[0]; // 获得方法参数列表中的第一个参数类型

    } else {

      type = method.getReturnType();  // 否则获取方法的返回类型

    }

  }

  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {

    return method.invoke(target, args); // 执行target的method方法

  }

  public Class getType() {

    return type;

  }

}

复制代码

MethodInvoker内部封装了method.invoke来执行方法。

3  reflection包-此包中的类基本是增强类,提供对外开放的API

3.1 Reflector类-class类的增强类

复制代码

public class Reflector {  // 反射器,class的增强类

  private static boolean classCacheEnabled = true;

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

  // 相当于缓存工厂,此处使用REFLECTOR_MAP目的是个人理解是因为Reflect的API很耗资源,所以用REFLECTOR_MAP将要反射的类及增强类放置在一起,以后使用时可以直接取不需要重复新建class的增强类了

  private static final Map, Reflector> REFLECTOR_MAP = new ConcurrentHashMap, Reflector>();

  private Class type; // 该类的类信息

  private String[] readablePropertyNames = EMPTY_STRING_ARRAY; // 可读属性

  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY; // 可写属性

  private Map setMethods = new HashMap(); // set方法

  private Map getMethods = new HashMap(); // get方法

  private Map> setTypes = new HashMap>(); // setxxx中xxx类型

  private Map> getTypes = new HashMap>(); // getxxx中的xxx类型

  private Constructor defaultConstructor; // 该类的默认构造函数

  private Map caseInsensitivePropertyMap = new HashMap();

  ......

}

复制代码

查看Reflector的基本属性,主要对一个类按照反射包括的数据结构(方法,属性,构造方法)进行解析。如User类中有age,name属性,且有get,set方法,那木在初始化时会将这些属性和属性,方法等解析出来。注意此类有一个静态列表,用于存放已解析好的类。用于当做缓存使用。查看构造方法验证。

复制代码

private Reflector(Class clazz) {  // 构造函数初始化元数据信息

    type = clazz;

    addDefaultConstructor(clazz); // 添加构造函数

    addGetMethods(clazz); // 添加类的get方法

    addSetMethods(clazz); // 添加类的set方法

    addFields(clazz); // 添加属性

    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); // 获取getxxx中xxx集合

    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); // 获取setxxx中xxx集合

    for (String propName : readablePropertyNames) {

      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);

    }

    for (String propName : writeablePropertyNames) {

      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);

    }

  }

复制代码

分析较简单的添加构造函数方法,后面的添加get,set方法较为复杂,限于篇幅,就略过了。

复制代码

private void addDefaultConstructor(Class clazz) { // 添加反射类元数据的默认构造函数

    Constructor[] consts = clazz.getDeclaredConstructors(); // 获取所有构造函数

    for (Constructor constructor : consts) {

      if (constructor.getParameterTypes().length == 0) { // 找到无参构造函数,即默认构造函数

        if (canAccessPrivateMethods()) { // 若是private,则变为可写

          try {

            constructor.setAccessible(true);

          } catch (Exception e) {

            // Ignored. This is only a final precaution, nothing we can do.

          }

        }

        if (constructor.isAccessible()) {

          this.defaultConstructor = constructor; // 设置默认构造函数

        }

      }

    }

  }

复制代码

3.2 MetaClass类-Reflector类的增强类

该类主要对于复杂的语句进行拆解。如果需分析一个xxx字段在某个类中是否存在set方法,

对于一般的如age单属性,可以直接调用Reflector.hasSetter("age")来判断。但如果对于group.user.age这个多属性,需分析group中是否有user的set方法,如果有,则继续分析在user对象中是否存在age的set方法。该实现主要是基于上文分析的PropertyTokenizer类与Reflector类。下文分析MetaClass中重写的hasSetter方法来验证。

复制代码

// 判断name是否在对象中存在set方法

  public boolean hasSetter(String name) { 

    PropertyTokenizer prop = new PropertyTokenizer(name); // 对复杂语句进行拆解

    if (prop.hasNext()) { // 若是复杂语句

      if (reflector.hasSetter(prop.getName())) {  // 第一层解析的对象属性有get方法

        MetaClass metaProp = metaClassForProperty(prop.getName());  // 获取getxxx中xxx的类型,构建成一个MetaClass对象,方便递归

        return metaProp.hasSetter(prop.getChildren()); // 递归操作

      } else { // 如有一层没有get方法,就直接返回false

        return false;

      }

    } else {

      return reflector.hasSetter(prop.getName()); // 简单语句直接调用reflector的方法

    }

  }

复制代码

3.3 MetaObject类-对外提供的类

MetaObject类里面存放了真正的对象。前文所分析的都是些静态对象,没有真正涉及到实例对象。分析MetaObject对外接口,其实都是内部调用了ObjectWrapper的方法。分析ObjectWrapper很简单,此处结合一个简单demo,来理解MetaObject的作用。

复制代码

class User{

    private String name;

    private String age;

    // ..... 省略getName,setName,getAge,setAge方法

}

@Test

  public void shouldGetAndSetField() {

    User user = new User();

    MetaObject meta = SystemMetaObject.forObject(user);  // 利用实例对象构建一个MetaObject对象

    meta.setValue("name", "xiabing");  // 调用metaObject.setValue方法,实际调用的是objectWrapper方法

    assertEquals("xiabing", meta.getValue("name")); // 比较,获取name的属性

  }

复制代码

MetaObject是对外提供api的类。要了解具体的实现,还需继续分析下文的wrapper包。

4. wrapper包-对象装饰包

以下为个人理解:使用wrapper包来封装对象,对外开放统一的set,get方法。比如我们使用一个User对象,要设置名称则需调用user.setName("haha")方法,设置年龄需调用user.setAge("10")。这样对于mybatis可能不太友好,于是使用了wrapper类。只需wrapper.set("name","haha"),wrapper.set("age","10")这样统一的接口方式就能设置属性了。看起来确实简洁许多。

4.1 ObjectWrapper接口,提供基本对外开放的接口

复制代码

public interface ObjectWrapper { //对象装饰类

  Object get(PropertyTokenizer prop); // 获得属性

  void set(PropertyTokenizer prop, Object value); // 设置属性

  String findProperty(String name, boolean useCamelCaseMapping); //查找属性

  String[] getGetterNames(); // 获取getXXX中xxx的集合

  String[] getSetterNames();  // 获取setXXX中的xxx的集合

  Class getSetterType(String name); // 根据xxx获取setxxx中xxx的类型

  Class getGetterType(String name); // 根据xxx获取getxxx中xxx的类型

  boolean hasSetter(String name); // 查找是否存在setxxx方法

  boolean hasGetter(String name); // 查找是否存在getxxx方法

  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory); // 实例化属性的值


  boolean isCollection(); // 该对象是否是集合


  public void add(Object element);


  public void addAll(List element);

}

复制代码

4.2 BaseWrapper抽象类-提供集合属性的方法

此类教简单,暂且忽略掉。

4.3 BeanWrapper - 真实执行方法的类

此类是反射的关键,是真正调用反射执行get,set,method方法的类。分析其基本属性,注意继承了BaseWrapper,而BaseWrapper中也有一个属性是MetaObject,当时说了MetaObject真正调用方法的是BaseWrapper类,可见,两个对象是一一对应关系。

public class BeanWrapper extends BaseWrapper { // bean的封装类

  private Object object; // 真实对象

  private MetaClass metaClass; // 该对象的反射类的增强类

分析get属性方法方法

复制代码

  public Object get(PropertyTokenizer prop) {

    if (prop.getIndex() != null) { // 分析PropertyTokenizer可知,若index不为null,则代表是集合对象,限于篇幅,小伙伴可自行分析

      Object collection = resolveCollection(prop, object);

      return getCollectionValue(prop, collection);

    } else {

      return getBeanProperty(prop, object); // 调用内部方法

    }

  }

  private Object getBeanProperty(PropertyTokenizer prop, Object object) {

    try {

      Invoker method = metaClass.getGetInvoker(prop.getName()); // 根据属性拿到getFiledInvoker执行器

      try {

        return method.invoke(object, NO_ARGUMENTS); // 调用getFiledInvoker中的方法

      } 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);

    }

  }

复制代码

上面用到了Invoke的方法,Invoke分析见上文。可知面向接口编程的优越性,将getFiled,setFiled,method全都封装成了invoke类。

分析set属性方法

复制代码

public void set(PropertyTokenizer prop, Object value) {

    if (prop.getIndex() != null) { // 分析PropertyTokenizer可知,若index不为null,则代表是集合对象,限

      Object collection = resolveCollection(prop, object);

      setCollectionValue(prop, collection, value);

    } else {

      setBeanProperty(prop, object, value); //调用内部方法

    }

  }


  private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {

    try {

      Invoker method = metaClass.getSetInvoker(prop.getName()); // 拿到setFiledInvoker执行器

      Object[] params = {value}; // 获取参数

      try {

        method.invoke(object, params); // 执行setFiledInvoker方法,实际调用Filed.set()方法设置属性

      } catch (Throwable t) {

        throw ExceptionUtil.unwrapThrowable(t);

      }

    } catch (Throwable t) {

      throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);

    }

  }

深圳网站建设www.sz886.com

你可能感兴趣的:(myBatis源码解析-反射篇(4))