MyBatis源码解读之工具类

MyBatis

1. 目的

介绍MyBatis工具类,包括资源文件读取,反射器功能,解析工具类,了解了这些基础工具类,对后面阅读其余源码很是有帮助。html

2. 资源文件读取

资源文件读取工具类 位于项目 org.apache.ibatis.io 参考MyBatis源码结构java

image

2.1 Resources 功能介绍

类经过类加载器简化对资源的访问。主要实现读取classpath包下的资源文件node

经过搜索Resources被引用的地方,有2个用途:git

  1. 加载资源文件,基本上测试类中都有用到
  2. 经过类名,加载Class

如图: imagegithub

2.1.1 Resources 源码解析
/**
   * Returns a resource on the classpath as a Reader object
   * 将classpath下的资源文件以字符流对象返回
   *
   * @param resource The resource to find
   * @return The resource
   * @throws java.io.IOException If the resource cannot be found or read
   */
  public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
      reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
  }
  
  /**
   * Returns a resource on the classpath as a Stream object
   * 资源文件以文件流返回
   *
   * @param loader   The classloader used to fetch the resource
   * @param resource The resource to find
   * @return The resource
   * @throws java.io.IOException If the resource cannot be found or read
   */
  public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return in;
  }
  
  /**
   * Loads a class
   * 加载类
   *
   * @param className - the class to fetch
   * @return The loaded class
   * @throws ClassNotFoundException If the class cannot be found (duh!)
   */
  public static Class<?> classForName(String className) throws ClassNotFoundException {
    return classLoaderWrapper.classForName(className);
  }

Resources中实际读取资源文件都是经过 ClassLoaderWrapper 实现spring

2.1.2 ClassLoaderWrapper 源码解析
/***
 * A class to wrap access to multiple class loaders making them work as one
 * 多个ClassLoader包装在一块儿,当作一个使用
 * @author Clinton Begin
 */
public class ClassLoaderWrapper {

    // 省略部分代码
    
    URL getResourceAsURL(String resource, ClassLoader[] classLoader) {

    URL url;
    //遍历ClassLoader执行资源文件加载,找到了就直接返回
    for (ClassLoader cl : classLoader) {

      if (null != cl) {

        // look for the resource as passed in...
        url = cl.getResource(resource);

        // ...but some class loaders want this leading "/", so we'll add it
        // and try again if we didn't find the resource
        if (null == url) {
          url = cl.getResource("/" + resource);
        }

        // "It's always in the last place I look for it!"
        // ... because only an idiot would keep looking for it after finding it, so stop looking already.
        if (null != url) {
          return url;
        }

      }

    }

    // didn't find it anywhere.
    return null;

  }
  

  /**
   * 构造一个classLoader数组
   * @param classLoader
   * @return
   */
  ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
  }
}

2.2 VFS 功能介绍

在应用服务中提供很是简单API访问资源文件。不一样的应用服务器有不一样的文件结构,有些特殊的须要进行适配,例如:JBoss6VFS。 主要是实现package这种目录式文件加载 <package name="org.apache.ibatis.builder.mapper"/>sql

配置文件事例:数据库

<mappers>
    <package name="org.apache.ibatis.builder.mapper"/>
  </mappers>

VFS 为抽象类,定义了模板方法 List<String> list(URL url, String forPath) 由子类实现, VFS是一个静态单例模式express

2.2.1 VFS源码解析
public abstract class VFS {
  private static final Log log = LogFactory.getLog(VFS.class);

  /** Singleton instance holder. 单例模式 */
  private static class VFSHolder {
    static final VFS INSTANCE = createVFS();

  /**
     * 经过静态方法实例化
     * @return
     */
  @SuppressWarnings("unchecked")
    static VFS createVFS() {
    // Try the user implementations first, then the built-ins
    List<Class<? extends VFS>> impls = new ArrayList<Class<? extends VFS>>();
    impls.addAll(USER_IMPLEMENTATIONS);
    impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

    // Try each implementation class until a valid one is found
    VFS vfs = null;
    for (int i = 0; vfs == null || !vfs.isValid(); i++) {
      Class<? extends VFS> impl = impls.get(i);
      try {
        vfs = impl.newInstance();
        if (vfs == null || !vfs.isValid()) {
          if (log.isDebugEnabled()) {
            log.debug("VFS implementation " + impl.getName() +
              " is not valid in this environment.");
          }
        }
      } catch (InstantiationException e) {
        log.error("Failed to instantiate " + impl, e);
        return null;
      } catch (IllegalAccessException e) {
        log.error("Failed to instantiate " + impl, e);
        return null;
      }
    }

    if (log.isDebugEnabled()) {
      log.debug("Using VFS adapter " + vfs.getClass().getName());
    }

      return vfs;
    }
  }
  
  /**
   * 获取VFS单例实例
   * Get the singleton {@link VFS} instance. If no {@link VFS} implementation can be found for the
   * current environment, then this method returns null.
   */
  public static VFS getInstance() {
    return VFSHolder.INSTANCE;
  }
  
   /** Return true if the {@link VFS} implementation is valid for the current environment.
   * 模板方法由子类实现
   * */
  public abstract boolean isValid();

  /**
   * Recursively list the full resource path of all the resources that are children of the
   * resource identified by a URL.
   * 模板方法由子类实现
   * 
   * @param url The URL that identifies the resource to list.
   * @param forPath The path to the resource that is identified by the URL. Generally, this is the
   *            value passed to {@link #getResources(String)} to get the resource URL.
   * @return A list containing the names of the child resources.
   * @throws IOException If I/O errors occur
   */
  protected abstract List<String> list(URL url, String forPath) throws IOException;

  /**
   * Recursively list the full resource path of all the resources that are children of all the
   * resources found at the specified path.
   * 递归获取目录下的资源文件
   * 
   * @param path The path of the resource(s) to list.
   * @return A list containing the names of the child resources.
   * @throws IOException If I/O errors occur
   */
  public List<String> list(String path) throws IOException {
    List<String> names = new ArrayList<String>();
    for (URL url : getResources(path)) {
      names.addAll(list(url, path));
    }
    return names;
  }
}

2.3 ResolverUtil 功能介绍

ResoulverUtil 主要用于解析给定package目录下知足特定条件的class,从源码中能够看到,实际是调用VFS.getInstance().list(path) 解析apache

2.3.1 ResolverUtil 源码解析
/**
   * Scans for classes starting at the package provided and descending into subpackages.
   * Each class is offered up to the Test as it is discovered, and if the Test returns
   * true the class is retained.  Accumulated classes can be fetched by calling
   * {@link #getClasses()}.
   
   * 从提供的package开始扫描classes,而且递归扫描全部子package。
   * 每个class被发现时都会提供一个Test(验证器),若是验证返回true,这个class会被保存起来。经过经过{@link #getClasses()}获取累计的classes。
   *
   * @param test an instance of {@link Test} that will be used to filter classes
   * @param packageName the name of the package from which to start scanning for
   *        classes, e.g. {@code net.sourceforge.stripes}
   */
  public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);

    try {
      //获取目录下的全部文件
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          //验证是匹配条件的Class
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }

3. 反射器工具类

反射器功能位于项目 org.apache.ibatis.reflection 参考MyBatis源码结构

image

3.1 ObjectFactory 功能介绍

MyBatis使用ObjectFactory建立全部须要的新对象。

image

3.1.1 ObjectFactory 源码解析

JDK 反射构造器的简单封装

/**
 * 对象建立工厂,根据Class建立真实对象
 * @author Clinton Begin
 */
public class DefaultObjectFactory implements ObjectFactory, Serializable {

  private static final long serialVersionUID = -8855120656740914948L;

  @Override
  public <T> T create(Class<T> type) {
    return create(type, null, null);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    //解析出Type的具体类型
    Class<?> classToCreate = resolveInterface(type);
    // we know types are assignable
    //实例化class
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }

  @Override
  public void setProperties(Properties properties) {
    // no props for default
  }


  private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      //无参构造
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        return constructor.newInstance();
      }
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      //含参数构造
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      //构造异常,输出详细错误参数
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

  /**
   * 解析对象类型,模板方法子类能够重写
   */
  protected Class<?> resolveInterface(Class<?> type) {
    Class<?> classToCreate;
    if (type == List.class || type == Collection.class || type == Iterable.class) {
      classToCreate = ArrayList.class;
    } else if (type == Map.class) {
      classToCreate = HashMap.class;
    } else if (type == SortedSet.class) { // issue #510 Collections Support
      classToCreate = TreeSet.class;
    } else if (type == Set.class) {
      classToCreate = HashSet.class;
    } else {
      classToCreate = type;
    }
    return classToCreate;
  }

  @Override
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }

}

测试用例:

//无参构造
DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory();
Set set = defaultObjectFactory.create(Set.class);
Assert.assertTrue(" set should be HashSet", set instanceof HashSet);

//有参构造
DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory();
TestClass testClass = defaultObjectFactory.create(TestClass.class,
Arrays.<Class<?>>asList(String.class, Integer.class), Arrays.<Object>asList("foo", 0));

你也能够自定义ObjectFactory,经过如下配置使用,ExampleObjectFactory在MyBatis源码包

<configuration>
    <objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory">
        <property name="objectFactoryProperty" value="100"/>
    </objectFactory>
</configuration>

3.2 Reflector 功能介绍

这个类缓存了类定义信息集合,容许在属性名和getter/setter方法之间进行简单的映射
经过调用Reflector解刨对象,将属性和方法所有分离,经过属性名能够找到对应的getter/setter方法

image

3.2.1 Reflector 源码解析
public class Reflector {

  /**对象Class类型*/
  private final Class<?> type;
  /**可读的属性名数组*/
  private final String[] readablePropertyNames;
  /**可写的属性名数组*/
  private final String[] writeablePropertyNames;
  /**对象的全部set方法*/
  private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
  /**对象的全部get方法*/
  private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
  /**对象的全部set方法参数类型*/
  private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
  /**对象的全部get方法返回类型*/
  private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
  /**对象构造器*/
  private Constructor<?> defaultConstructor;
  /**忽略大小写的方式存储属性名,key转大写后存储,value为真实属性名,用于快速查找*/
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();

  /***
   * #mark 反射构造对象
   * @param clazz
   */
  public Reflector(Class<?> clazz) {
    type = clazz;
    //构造方法
    addDefaultConstructor(clazz);
    //全部get方法
    addGetMethods(clazz);
    //全部set方法
    addSetMethods(clazz);
    //成员变量
    addFields(clazz);
    
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

  //省略其余代码
}

从类图能够看出DefaultReflectorFactory依赖Reflector,那咱们来看看DefaultReflectorFactory具体实现

3.2.2 DefaultReflectorFactory 源码解析
/**
 * 反射器工厂
 */
public class DefaultReflectorFactory implements ReflectorFactory {

  /**反射器是否须要缓存,默认须要*/
  private boolean classCacheEnabled = true;

  /**类与反射器对象映射*/
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      //类反射器是否存在,不存在实例化,存在则返回
      Reflector cached = reflectorMap.get(type);
      if (cached == null) {
        cached = new Reflector(type);
        reflectorMap.put(type, cached);
      }
      return cached;
    } else {
      return new Reflector(type);
    }
  }
}

测试用例:

@Test
  public void testGetSetterType() throws Exception {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    //获取Section反射器对象
    Reflector reflector = reflectorFactory.findForClass(Section.class);
    //验证属性id的set方法为Long.class类型
    Assert.assertEquals(Long.class, reflector.getSetterType("id"));
  }

  @Test
  public void testGetGetterType() throws Exception {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    Reflector reflector = reflectorFactory.findForClass(Section.class);
    //验证属性id的get方法为Long.class类型
    Assert.assertEquals(Long.class, reflector.getGetterType("id"));
  }

  @Test
  public void shouldNotGetClass() throws Exception {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    Reflector reflector = reflectorFactory.findForClass(Section.class);
    //验证是否存在getClass方法
    Assert.assertFalse(reflector.hasGetter("class"));
  }
  

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

  static class Section extends AbstractEntity implements Entity<Long> {
  }

更多的调用 查看源码 ReflectorTest

4. 反射器元编程

4.1 MetaObject 功能介绍

元对象,操做对象的对象

为何会叫作操做对象的对象呢?
MetaObject从字面翻译为元对象。将对象转换为MetaObject对象后,就能够经过MetaObject操做源对象的全部操做。能够理解MetaObject为操做对象的对象。有点相似元编程,关与元编程能够参考知乎回答

image

4.1.1 MetaObject 源码解析
/**
 * 元对象,操做对象的对象
 * @author Clinton Begin
 */
public class MetaObject {

  /**原始对象*/
  private final Object originalObject;
  /**对象包装器*/
  private final ObjectWrapper objectWrapper;
  /**对象工厂*/
  private final ObjectFactory objectFactory;
  /**对象包装器工厂*/
  private final ObjectWrapperFactory objectWrapperFactory;
  /**反射器工厂*/
  private final ReflectorFactory reflectorFactory;

  /**
   * 私有的元对象构造方法
   * @param object
   * @param objectFactory
   * @param objectWrapperFactory
   * @param reflectorFactory
   */
  private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;
    this.reflectorFactory = reflectorFactory;

    //经过原始对象类型获取对象包装器
    if (object instanceof ObjectWrapper) {
      this.objectWrapper = (ObjectWrapper) object;
    } else if (objectWrapperFactory.hasWrapperFor(object)) {
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    } else if (object instanceof Map) {
      this.objectWrapper = new MapWrapper(this, (Map) object);
    } else if (object instanceof Collection) {
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }

  /**
   * 获取对象的元对象
   * @param object
   * @param objectFactory
   * @param objectWrapperFactory
   * @param reflectorFactory
   * @return
   */
  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    if (object == null) {
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
  }
  
  //省略部分代码

    /**
     * 获取属性值
     * @param name
     * @return
     */
  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);
    }
  }

    /**
     * 设置属性值
     * @param name
     * @param value
     */
  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) {
          // 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);
    }
  }

    /**
     * 获取属性的元对象,由于对象属性也是对象
     * @param name
     * @return
     */
  public MetaObject metaObjectForProperty(String name) {
    Object value = getValue(name);
    return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
  }

}

测试用例:

/**
 * 测试对象
 */
public class RichType {

  /** 嵌套属性 */
  private RichType richType;

  private String richField;

  private String richProperty;

  private Map richMap = new HashMap();

  private List richList = new ArrayList() {
    {
      add("bar");
    }
  };
  
  //省略getter/setter
}

/**
 * 元对象测试
 */
public class MetaObjectTest {

  /**
   * 属性赋值和取值
   */
  @Test
  public void shouldGetAndSetField() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richField", "foo");
    assertEquals("foo", meta.getValue("richField"));
  }

  /**
   * 嵌套属性赋值和取值
   */
  @Test
  public void shouldGetAndSetNestedField() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richField", "foo");
    assertEquals("foo", meta.getValue("richType.richField"));
  }

  /**
   * 嵌套对象的Map属性赋值和取值
   */
  @Test
  public void shouldGetAndSetNestedMapPair() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richMap.key", "foo");
    assertEquals("foo", meta.getValue("richType.richMap.key"));
  }

  /**
   * 属性集合赋值和取值
   */
  @Test
  public void shouldGetAndSetListItem() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richList[0]", "foo");
    assertEquals("foo", meta.getValue("richList[0]"));
  }
  
  //省略不少代码
}

SystemMetaObject 是MetaObject的工具类,这里就不列出来了。具体功能请下载源码查看

4.1.2 MetaObject 应用
  1. 设置查询参数值

DefaultParameterHandler 代码

/**
   * #mark 设置执行参数
   *
   * @param ps
   */
  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        //参数非输出参数
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            //参数为外部参数
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            //参数默认处理器能够处理
            value = parameterObject;
          } else {
            //获取元数据对象
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            //经过对象获值
            value = metaObject.getValue(propertyName);
          }
          //获取参数类型处理器,不一样类型参数调用不一样setXXX(i,value)
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }
  1. 遍历查询结果集赋值到对象

DefaultResultSetHandler 代码

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) {

    // 省略部分代码
    
    if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
              // gcode issue #377, call setter on nulls (value is not 'found')
              //经过元对象为属性设置值
              metaObject.setValue(property, value);
            }
            
}

4.2 MetaClass 功能介绍

元类,操做类的类。
MetaClass和MetaObject相似,但侧重点是对类的操做,MetaClass封装了Reflector的功能

image

4.2.1 MetaClass 源码解析
/**
 * 元类,操做类的类
 * @author Clinton Begin
 */
public class MetaClass {

  /**反射器工厂*/
  private final ReflectorFactory reflectorFactory;
  /**反射器*/
  private final Reflector reflector;

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

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

  //省略其余代码
}

测试用例:

/**
 * 元类测试
 */
public class MetaClassTest {

  @Test
  public void shouldTestDataTypeOfGenericMethod() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    //获取元类对象
    MetaClass meta = MetaClass.forClass(GenericConcrete.class, reflectorFactory);
    //属性id的返回值为Long类型
    assertEquals(Long.class, meta.getGetterType("id"));
    assertEquals(Long.class, meta.getSetterType("id"));
  }

  /**
   * 验证全部属性getter返回参数类型
   */
  @Test
  public void shouldCheckTypeForEachGetter() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    //验证对象成员变量
    assertEquals(String.class, meta.getGetterType("richField"));
    //验证对象属性
    assertEquals(String.class, meta.getGetterType("richProperty"));
    //集合属性
    assertEquals(List.class, meta.getGetterType("richList"));
    assertEquals(Map.class, meta.getGetterType("richMap"));
    assertEquals(List.class, meta.getGetterType("richList[0]"));

    assertEquals(RichType.class, meta.getGetterType("richType"));
    //验证嵌套属性对象的成员变量
    assertEquals(String.class, meta.getGetterType("richType.richField"));
    assertEquals(String.class, meta.getGetterType("richType.richProperty"));
    assertEquals(List.class, meta.getGetterType("richType.richList"));
    assertEquals(Map.class, meta.getGetterType("richType.richMap"));
    assertEquals(List.class, meta.getGetterType("richType.richList[0]"));
  }

  //省略其余代码

}
4.2.2 MetaClass 应用
  1. 解析配置属性对应对的Java类型

MapperBuilderAssistant 代码

/**
   * 解析结果属性对应的Java类型
   * @param resultType
   * @param property
   * @param javaType
   * @return
   */
  private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
    if (javaType == null && property != null) {
      try {
        MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());
        javaType = metaResultType.getSetterType(property);
      } catch (Exception e) {
        //ignore, following null check statement will deal with the situation
      }
    }
    if (javaType == null) {
      javaType = Object.class;
    }
    return javaType;
  }

  /**
   * 解析参数对应的Java类型
   * @param resultType
   * @param property
   * @param javaType
   * @param jdbcType
   * @return
   */
  private Class<?> resolveParameterJavaType(Class<?> resultType, String property, Class<?> javaType, JdbcType jdbcType) {
    if (javaType == null) {
      if (JdbcType.CURSOR.equals(jdbcType)) {
        javaType = java.sql.ResultSet.class;
      } else if (Map.class.isAssignableFrom(resultType)) {
        javaType = Object.class;
      } else {
        MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());
        javaType = metaResultType.getGetterType(property);
      }
    }
    if (javaType == null) {
      javaType = Object.class;
    }
    return javaType;
  }
  1. 辅助构造查询结果集的真实对象。

DefaultResultSetHandler 代码

//#mark 建立结果对象,结果集对应Java实体对象
  private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
      throws SQLException {
    //真实对象类型
    final Class<?> resultType = resultMap.getType();
    //获取反射器元数据类
    final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
    final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
    if (hasTypeHandlerForResultObject(rsw, resultType)) {
      //是否有类型处理器
      return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
    } else if (!constructorMappings.isEmpty()) {
      //使用构造方法参数,构建结果对象
      return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
    } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
      //接口或有默认构造
      return objectFactory.create(resultType);
    } else if (shouldApplyAutomaticMappings(resultMap, false)) {
      return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
    }
    throw new ExecutorException("Do not know how to create an instance of " + resultType);
  }

5. 解析工具类

解析工具类 位于项目 org.apache.ibatis.parsing

5.1 GenericTokenParser 功能介绍

通用的占位符解析器,基于GenericTokenParser能够实现SQL占位符#{}、${} 参数的替换

SqlSourceBuilder 代码实例

/**
        * #mark 解析出真实的SQL,将id=#{id} 转换成 id=?
        * @param originalSql
        * @param parameterType
        * @param additionalParameters
        * @return
    */
    public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
        GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
        String sql = parser.parse(originalSql);
        return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
    }
5.1.1 GenericTokenParser 源码解析
/**
 * #mark 通用的占位符解析
 * @author Clinton Begin
 */
public class GenericTokenParser {

  /**占位符起始符号 如: ${ */
  private final String openToken;
  /**占位符结束符号 如: } */
  private final String closeToken;
  /**占位符处理器*/
  private final TokenHandler handler;

  public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
    this.openToken = openToken;
    this.closeToken = closeToken;
    this.handler = handler;
  }

  /**
   * 解析
   * @param text
   * @return
   */
  public String parse(String text) {
    //省略功能实现
  }
}

测试用例:

/**
 * 通用的占位符解析测试
 */
public class GenericTokenParserTest {

  /**
   * 占位符处理器,实现简单的从Map中获取值
   */
  public static class VariableTokenHandler implements TokenHandler {
    private Map<String, String> variables = new HashMap<String, String>();

    /**
     * 处理器初始化,传入map变量
     * @param variables
     */
    public VariableTokenHandler(Map<String, String> variables) {
      this.variables = variables;
    }

    /**
     * 占位符处理
     * @param content
     * @return
     */
    @Override
    public String handleToken(String content) {
      //返回map值
      return variables.get(content);
    }
  }

  @Test
  public void shouldDemonstrateGenericTokenReplacement() {
    GenericTokenParser parser = new GenericTokenParser("${", "}", new VariableTokenHandler(new HashMap<String, String>() {
      {
        //初始化占位符数据字典
        put("first_name", "James");
        put("initial", "T");
        put("last_name", "Kirk");
        put("var{with}brace", "Hiya");
        put("", "");
      }
    }));
    
    assertEquals("James T Kirk reporting.", parser.parse("${first_name} ${initial} ${last_name} reporting."));
  }
  
  //省略其余代码
  }

5.2 PropertyParser 功能介绍

解析配置文件 ${} 变量
咱们常使用这种占位符方式配置数据库链接池

5.2.1 PropertyParser 源码解析
/**
 * 解析配置文件 ${} 变量
 *
 * @author Clinton Begin
 * @author Kazuki Shimizu
 */
public class PropertyParser {

  private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
  /**
   * 特别的属性key,用因而否显示默认值的占位符
   
   * @since 3.4.2
   */
  public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";

  /**
   * #mark 特别的属性key,用于分割 key 和 默认值的占位符
   * @since 3.4.2
   */
  public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";

  /**
   * 是否开启默认值
   */
  private static final String ENABLE_DEFAULT_VALUE = "false";

  /**
   * 默认值分隔符
   */
  private static final String DEFAULT_VALUE_SEPARATOR = ":";

  private PropertyParser() {
    // Prevent Instantiation
  }

  /**
   * 解析
   * @param string 解析的字符串
   * @param variables
   * @return
   */
  public static String parse(String string, Properties variables) {
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
  }

  /**
   * 占位符替换处理器
   */
  private static class VariableTokenHandler implements TokenHandler {
    private final Properties variables;
    /**是否开启默认值*/
    private final boolean enableDefaultValue;
    /**默认分隔符*/
    private final String defaultValueSeparator;

    private VariableTokenHandler(Properties variables) {
      this.variables = variables;
      this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
      this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
    }

    private String getPropertyValue(String key, String defaultValue) {
      return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
    }

    @Override
    public String handleToken(String content) {
      if (variables != null) {
        String key = content;
        //开启默认值
        if (enableDefaultValue) {
          final int separatorIndex = content.indexOf(defaultValueSeparator);
          String defaultValue = null;
          //分隔符存在
          if (separatorIndex >= 0) {
            //截取分隔符前面的key
            key = content.substring(0, separatorIndex);
            //截取默认值
            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
          }
          //存在默认值
          if (defaultValue != null) {
            //获取properties对应的值,值不存在则返回默认值
            return variables.getProperty(key, defaultValue);
          }
        }
        //未开启默认值
        if (variables.containsKey(key)) {
          return variables.getProperty(key);
        }
      }
      //以上都未匹配返回
      return "${" + content + "}";
    }
  }
}

测试用例
配置文件:

<configuration>
	
	<settings>
		<setting name="jdbcTypeForNull" value="${settings:jdbcTypeForNull?:NULL}"/>
	</settings>

	<objectFactory type="org.apache.ibatis.submitted.global_variables_defaults.SupportClasses$CustomObjectFactory">
		<property name="name" value="${objectFactory:name?:default}"/>
	</objectFactory>

	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="UNPOOLED">
				<property name="driver" value="org.hsqldb.jdbcDriver" />
				<property name="url" value="jdbc:hsqldb:mem:${db:name?:global_variables_defaults}" />
				<property name="username" value="sa" />
			</dataSource>
		</environment>
	</environments>
	
	<databaseIdProvider type="DB_VENDOR">
		<property name="${productName:hsql?:HSQL Database Engine}" value="hsql"/>
	</databaseIdProvider>

</configuration>

代码:

public class CustomizationTest {

   /**
   * 注解式的Mapper对象
   */
  @CacheNamespace(implementation = SupportClasses.CustomCache.class, properties = {
      @Property(name = "name", value = "${cache:name?:default}")
  })
  private interface CustomDefaultValueSeparatorMapper {
    @Select("SELECT '${val != null ? val : 'default'}' FROM INFORMATION_SCHEMA.SYSTEM_USERS")
    String selectValue(@Param("val") String val);
  }

  @Test
  public void applyDefaultValueWhenCustomizeDefaultValueSeparator() throws IOException {
    //定义properties对象值
    Properties props = new Properties();
    //开启默认值配置
    props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
    //分隔符使用 ?:
    props.setProperty(PropertyParser.KEY_DEFAULT_VALUE_SEPARATOR, "?:");

    Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/global_variables_defaults/mybatis-config-custom-separator.xml");
    //实例化SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
    Configuration configuration = factory.getConfiguration();
    //添加Mapper对象
    configuration.addMapper(CustomDefaultValueSeparatorMapper.class);
    //测试自定义缓存对象
    SupportClasses.CustomCache cache = SupportClasses.Utils.unwrap(configuration.getCache(CustomDefaultValueSeparatorMapper.class.getName()));

    //验证配置解析结果
    Assertions.assertThat(configuration.getJdbcTypeForNull()).isEqualTo(JdbcType.NULL);
    Assertions.assertThat(((UnpooledDataSource) configuration.getEnvironment().getDataSource()).getUrl())
        .isEqualTo("jdbc:hsqldb:mem:global_variables_defaults");
    Assertions.assertThat(configuration.getDatabaseId()).isEqualTo("hsql");
    Assertions.assertThat(((SupportClasses.CustomObjectFactory) configuration.getObjectFactory()).getProperties().getProperty("name"))
        .isEqualTo("default");
    Assertions.assertThat(cache.getName()).isEqualTo("default");
    //开启SqlSession
    SqlSession sqlSession = factory.openSession();
    try {
      CustomDefaultValueSeparatorMapper mapper = sqlSession.getMapper(CustomDefaultValueSeparatorMapper.class);
      //执行selectValue
      Assertions.assertThat(mapper.selectValue(null)).isEqualTo("default");
    } finally {
      sqlSession.close();
    }
  }
}

5.3 XPathParser 功能介绍

XPath形式XML解析器 XPathParser 包揽了MyBatis全部的XML文件解析

2个重要的配置文件解析入口,代码实例:

/**
   * #mark 配置解析入口 #20170821
   * http://www.mybatis.org/mybatis-3/zh/configuration.html
   * @return
   */
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  
/**
   * 解析 mapper
   * http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html
   */
  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }
5.3.1 XPathParser 源码解析
/**
 * XPath形式XML解析器
 * @author Clinton Begin
 */
public class XPathParser {

  /** dom 对象 */
  private final Document document;
  /** 是否验证配置文件 */
  private boolean validation;
  /** 实体解析器,包含XML语法定义。具体查看 {@link org.apache.ibatis.builder.xml.XMLMapperEntityResolver} */
  private EntityResolver entityResolver;
  /** properties 变量  */
  private Properties variables;

  private XPath xpath;

  p/**
   * 构造器
   * @param reader
   * @param validation
   * @param variables
   * @param entityResolver
   */
  public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(reader));
  }

  /**
   * 解析XML节点集合
   * @param expression
   * @return
   */
  public List<XNode> evalNodes(String expression) {
    return evalNodes(document, expression);
  }

  public List<XNode> evalNodes(Object root, String expression) {
    List<XNode> xnodes = new ArrayList<XNode>();
    NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
    for (int i = 0; i < nodes.getLength(); i++) {
      xnodes.add(new XNode(this, nodes.item(i), variables));
    }
    return xnodes;
  }

  /**
   * 解析XML节点
   * @param expression
   * @return
   */
  public XNode evalNode(String expression) {
    return evalNode(document, expression);
  }


  /**
   * 建立Document
   * @param inputSource
   * @return
   */
  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      //设置解析器
      builder.setEntityResolver(entityResolver);
      //XML文件验证错误处理
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      //XML 配置错误
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }
  
  //省略其余代码

}

测试用例

配置文件:

<employee id="${id_var}">
  <blah something="that"/>
  <first_name>Jim</first_name>
  <last_name>Smith</last_name>
  <birth_date>
    <year>1970</year>
    <month>6</month>
    <day>15</day>
  </birth_date>
  <height units="ft">5.8</height>
  <weight units="lbs">200</weight>
  <active>true</active>
</employee>

测试代码:

public class XPathParserTest {

  @Test
  public void shouldTestXPathParserMethods() throws Exception {
    String resource = "resources/nodelet_test.xml";
    //读取资源文件
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //实例XPathParser对象
    XPathParser parser = new XPathParser(inputStream, false, null, null);
    //获取 <employee><birth_date><year> 节点值
    assertEquals((Long)1970l, parser.evalLong("/employee/birth_date/year"));
    //获取 <employee id> id属性值
    assertEquals("${id_var}", parser.evalString("/employee/@id"));
    // 省略其余代码
    inputStream.close();
  }
}

参考资料


关于MyBatis源码解读之工具类就介绍到这里。若有疑问,欢迎留言,谢谢。

相关文章
相关标签/搜索