本文在MyBatis主流程分析之(三)-准备SQL语句和参数替换、执行的基础上解释了mybatis如何利用反射获取和设置参数对象上的值。

本文涉及java基础的反射,对反射不怎么了解的可以参考java反射 ,mybatis最终也是利用java的反射机制来获取和设置对象的值得。

基本的原理
- 方法一:通过属性设置或获取

Class<?> demo = null;
        Object obj = null;

        demo = Class.forName("Reflect.Person");
        obj = demo.newInstance();

        Field field = demo.getDeclaredField("sex");
        field.setAccessible(true);
        field.set(obj, "男");
        System.out.println(field.get(obj));
  • 方法二:通过方法设置或获取
public static void getter(Object obj, String att) {
     try {
         Method method = obj.getClass().getMethod("get" + att);
         System.out.println(method.invoke(obj));
     } catch (Exception e) {
         e.printStackTrace();
     }
     }


     public static void setter(Object obj, String att, Object value,
     Class<?> type) {
     try {
         Method method = obj.getClass().getMethod("set" + att, type);
         method.invoke(obj, value);
     } catch (Exception e) {
         e.printStackTrace();
     }
     }


    /**
     * @param args
     */
    public static void main(String[] args) {
        Class<?> demo = null;
        Object obj=null;
        try {
            demo = Class.forName("com.elements.Reflect.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try{
         obj=demo.newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        setter(obj,"Sex","男",String.class);
        getter(obj,"Sex");


    }

以下的例子和分析中我们着重了pojo(bean)的方式,其他map和集合的方式这里没有分析。

我们首先看看mybatis的reflection包下的所有类

java反射mybatis查询 mybatis的反射原理_java反射mybatis查询

一、Invoker包

这里也是mybatis最终利用java反射模式获取和设置对象值得地方。
Invoker接口定义

public interface Invoker {

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

  Class<?> getType();
}

在这个包下面有三个实现类,分别是GetFieldInvoker,SetFieldInvoker和MethodInvoker。GetFieldInvoker和SetFieldInvoker分别调用Field的get和set用来获取和设置对象的属性值。MethodInvoker用来调用Object的的某个方法(通过方法设置对象的属性)。

  • GetFieldInvoker类
public class GetFieldInvoker implements Invoker {
  private Field field;

  public GetFieldInvoker(Field field) {
    this.field = field;
  }

  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
    return field.get(target);//还是调用java最基本模式
  }

  public Class<?> getType() {
    return field.getType();
  }
}
  • SetFieldInvoker
public class SetFieldInvoker implements Invoker {
  private Field field;

  public SetFieldInvoker(Field field) {
    this.field = field;
  }

  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
    field.set(target, args[0]);//还是调用java最基本模式
    return null;
  }

  public Class<?> getType() {
    return field.getType();
  }
}
  • MethodInvoker
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);//调用对象的方法
  }

  public Class<?> getType() {
    return type;
  }
}

二、Property包

  • PropertyNamer
    定义几个静态方法,方便下面几个类使用。
//这个方法把访问方法的名字转换成范围属性的方式,先截取了get和set再将首字母变成小写。
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;
  }

  //判断是否属性
  public static boolean isProperty(String name) {
    return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
  }

  //判断是否是一个get  方法
  public static boolean isGetter(String name) {
    return name.startsWith("get") || name.startsWith("is");
  }
 //判断是否是一个set 方法
  public static boolean isSetter(String name) {
    return name.startsWith("set");
  }
  • 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));
        } catch (Exception e) {
          // Nothing useful to do, will only fail on final fields, which will be ignored.
        }
      }
      parent = parent.getSuperclass();
    }
  }
  • PropertyTokenizer(属性标记器)
    传入:User[1].age
    name=User
    children=age
    index=1
private String name;
  private String indexedName;
  private String index;
  private String children;

  public PropertyTokenizer(String fullname) {
    // 对参数进行第一次处理,通过“.”分隔符将propertyName分作两部分
    int delim = fullname.indexOf('.');
    if (delim > -1) {
      name = fullname.substring(0, delim);
      children = fullname.substring(delim + 1);
    } else {
      name = fullname;
      children = null;
    }
    indexedName = name;
    // 对name进行二次处理,去除“[...]”,并将方括号内的内容赋给index属性,如果name属性中包含“[]”的话
    delim = name.indexOf('[');
    if (delim > -1) {
      index = name.substring(delim + 1, name.length() - 1);
      name = name.substring(0, delim);
    }
  }

三、Wrapper包装

java反射mybatis查询 mybatis的反射原理_mybatis_02

这里主要是实现了图片左边的ObjectWrapper定义的内容

ObjectWrapper的接口定义

public interface ObjectWrapper {

  //获取属性的值
  Object get(PropertyTokenizer prop);

  //设置属性的值
  void set(PropertyTokenizer prop, Object value);
   //查找某属性
  String findProperty(String name, boolean useCamelCaseMapping);
  //获取所有get的名字
  String[] getGetterNames();
  //获取所有set的名字
  String[] getSetterNames();

  //set的类型
  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();
  //集合的增加
  public void add(Object element);
  //结合的增加
  public <E> void addAll(List<E> element);

}

这里我们简单看一下 BeanWrapper的实现

private Object object;
  private MetaClass metaClass;

  //构造函数
  public BeanWrapper(MetaObject metaObject, Object object) {
    super(metaObject);
    this.object = object;
    this.metaClass = MetaClass.forClass(object.getClass());
  }

  //获取属性值
  public Object get(PropertyTokenizer prop) {
    //根据prop.getIndex判断是否一个一个集合
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      //下面调用的是BaseWrapper的方法
      return getCollectionValue(prop, collection);
    } else {
      return getBeanProperty(prop, object);
    }
  }
  //设置属性的值
  public void set(PropertyTokenizer prop, Object value) {
    //根据prop.getIndex判断是否一个一个集合
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      //下面调用的是BaseWrapper的方法
      setCollectionValue(prop, collection, value);
    } else {
      setBeanProperty(prop, object, value);
    }
  }

设置和获取值得方法。

private Object getBeanProperty(PropertyTokenizer prop, Object object) {
    try {
      Invoker method = metaClass.getGetInvoker(prop.getName());
      try {
        return method.invoke(object, NO_ARGUMENTS);
      } 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);
    }
  }

  private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
      Invoker method = metaClass.getSetInvoker(prop.getName());
      Object[] params = {value};
      try {
        method.invoke(object, params);
      } 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);
    }
  }

在这里我们可以看到设置和获取值都是通过MetaClass对象返回一个方法,通过这个方法获取或者设置值的。这个对象在构造这个BeanWrapper对象是就已经初始化好了。
this.metaClass = MetaClass.forClass(object.getClass());
接下来我们看看这个MetaClass类

四、MetaClass类

java反射mybatis查询 mybatis的反射原理_属性值_03

在这个类中定义了一个Reflector 类,MetaClass通过这个类实现了getGetInvoker和getSetInvoker这两个方法。

private Reflector reflector;

  private MetaClass(Class<?> type) {
    this.reflector = Reflector.forClass(type);
  }

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


  public Invoker getGetInvoker(String name) {
    return reflector.getGetInvoker(name);
  }

  public Invoker getSetInvoker(String name) {
    return reflector.getSetInvoker(name);
  }

五、Reflector

private Class<?> type;//反射的对象参数的类
  private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
  private Map<String, Invoker> setMethods = new HashMap<String, Invoker>();//set方法
  private Map<String, Invoker> getMethods = new HashMap<String, Invoker>();//get方法
  private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();//set方法的返回类
  private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();//get方法的返回类
  private Constructor<?> defaultConstructor;//构造函数

  private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();//所有属性的字段

  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()]);
         //所有写的属性名字
         writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
         //下面都把属性的名字存储到了caseInsensitivePropertyMap 中
         for (String propName : readablePropertyNames) {
           caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
         }
         for (String propName : writeablePropertyNames) {
           caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
  }

  public static Reflector forClass(Class<?> clazz) {
    if (classCacheEnabled) {
      // synchronized (clazz) removed see issue #461
      Reflector cached = REFLECTOR_MAP.get(clazz);
      if (cached == null) {
        cached = new Reflector(clazz);
        REFLECTOR_MAP.put(clazz, cached);
      }
      return cached;
    } else {
      return new Reflector(clazz);
    }
  }

增加所有包含get的方法

private void addGetMethods(Class<?> cls) {
    Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
    Method[] methods = getClassMethods(cls);
    for (Method method : methods) {
      String name = method.getName();
      if (name.startsWith("get") && name.length() > 3) {
        if (method.getParameterTypes().length == 0) {
          //这里的名字首字符已经变成了小写
          name = PropertyNamer.methodToProperty(name);
          addMethodConflict(conflictingGetters, name, method);
        }
      } else if (name.startsWith("is") && name.length() > 2) {
        if (method.getParameterTypes().length == 0) {
          //这里的名字首字符已经变成了小写
          name = PropertyNamer.methodToProperty(name);
          addMethodConflict(conflictingGetters, name, method);
        }
      }
    }
    resolveGetterConflicts(conflictingGetters);
  }

增加所有包含set的方法

private void addSetMethods(Class<?> cls) {
    Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
    Method[] methods = getClassMethods(cls);
    for (Method method : methods) {
      String name = method.getName();
      if (name.startsWith("set") && name.length() > 3) {
        if (method.getParameterTypes().length == 1) {
          name = PropertyNamer.methodToProperty(name);
          addMethodConflict(conflictingSetters, name, method);
        }
      }
    }
    resolveSetterConflicts(conflictingSetters);
  }

所有的属性字段

private void addFields(Class<?> clazz) {
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
      if (canAccessPrivateMethods()) {
        try {
          field.setAccessible(true);
        } catch (Exception e) {
          // Ignored. This is only a final precaution, nothing we can do.
        }
      }
      if (field.isAccessible()) {
        //setMethods的方法中不包括某个属性时候再增加到setMethods中
        if (!setMethods.containsKey(field.getName())) {
          // issue #379 - removed the check for final because JDK 1.5 allows
          // modification of final fields through reflection (JSR-133). (JGB)
          // pr #16 - final static can only be set by the classloader
          int modifiers = field.getModifiers();
          if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
            addSetField(field);
          }
        }
         //getMethods的方法中不包括某个属性时候再增加getMethods中
        if (!getMethods.containsKey(field.getName())) {
          addGetField(field);
        }
      }
    }
    if (clazz.getSuperclass() != null) {
      addFields(clazz.getSuperclass());
    }
  }

最后在看看这个类的getSetInvoker和getGetInvoker

public Invoker getSetInvoker(String propertyName) {
    Invoker method = setMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
    }
    return method;
  }

  public Invoker getGetInvoker(String propertyName) {
    Invoker method = getMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    }
    return method;
  }

这里根据属性的名字在getMethods或者setMethods的Map获取相应的方法。

六、获取和设置bean的值某个属性值

这里我们通过一个Testmete 类来模拟了MyBatis的获取和设置参数对象的值。
- User类

public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return UserName;
    }
    public void setUserName(String userName) {
        UserName = userName;
    }
    public String getUserEmail() {
        return UserEmail;
    }
    public void setUserEmail(String userEmail) {
        UserEmail = userEmail;
    }
    private int userId;
    private  String UserName;
    private  String UserEmail;


    @Override
    public String toString() {
        return "User [userId=" + userId + ", UserName=" + UserName
                + ", UserEmail=" + UserEmail + "]";
    }
  • 测试类
package com.elements.mete;

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.junit.Test;

import com.elements.user.model.User;

public class Testmete {


     protected ObjectFactory objectFactory = new DefaultObjectFactory();
     protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

    @Test
    public void TestMetaObject() {
        User user = new User();
        user.setUserEmail("likewindy@123.com");
        user.setUserName("likewindy");
        MetaObject metaObject = newMetaObject(user);
        //获取值方法模式
        Object value = metaObject.getValue("userName");
        //获取值Field模式
        Object value2 = metaObject.getValue("UserName");
        System.out.println(value);
        //设置UserEmail的值
        metaObject.setValue("UserEmail", "123456@123.com");
        System.out.println(user.getUserEmail());
    }
}

获取bean的属性顺序图

java反射mybatis查询 mybatis的反射原理_java反射mybatis查询_04

最后我们在看这个MetaObject类

七、MetaObject类

它是mybatis对外的接口

private Object originalObject;//例如一个pojo对象,例如User对象类。
  private ObjectWrapper objectWrapper;//根据参数对象的不同,在构造函数中配置
  private ObjectFactory objectFactory;//Configuration中配置
  private ObjectWrapperFactory objectWrapperFactory;//Configuration中配置

    //构造函数,私有
    private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;

    if (object instanceof ObjectWrapper) {
       //如果参数对象实现了ObjectWrapper
      this.objectWrapper = (ObjectWrapper) object;
    } else if (objectWrapperFactory.hasWrapperFor(object)) {
      //如果objectWrapperFactory已经包装了对象,对用objectWrapperFactory的getWrapperFor
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    } else if (object instanceof Map) {
      //是一个Map对象,使用mybatis的MapWrapper
      this.objectWrapper = new MapWrapper(this, (Map) object);
    } else if (object instanceof Collection) {
      //是一个CollectionWrapper对象
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {
      //其他默认使用BeanWrapper
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }
  //对外公开的接口object是我们要获取或设置的对象
  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    if (object == null) {
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory);
    }
  }

  public Object getValue(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        return null;
      } else {
        return metaValue.getValue(prop.getChildren());
      }
    } else {
      return objectWrapper.get(prop);
    }
  }

  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 && prop.getChildren() != null) {
          return; // don't instantiate child path if value is null
        } else {
          metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
        }
      }
      metaValue.setValue(prop.getChildren(), value);
    } else {
      objectWrapper.set(prop, value);
    }
  }

无论是set方法还是get方法它都是调用了objectWrapper的方法。这个objectWrapper就是根据参数对象的不同调用不同的对象,例如map包装对象MapWrapper,集合包装对象CollectionWrapper,和bean包装对象BeanWrapper。

在这里mybatis预留两个接口对象,可以自己封装。

  1. 参数对象实现了ObjectWrapper接口
  2. 实现了ObjectWrapperFactory的接口,并且通过配置应用到了Configuration类中。 它有一个默认的实现类DefaultObjectWrapperFactory
public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {

  public boolean hasWrapperFor(Object object) {
    return false;
  }

  public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
    throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
  }

}