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源碼解讀之工具類就介紹到這裏。若有疑問,歡迎留言,謝謝。

相關文章
相關標籤/搜索