mybatis 源碼分析之 BaseBuilder

Nel92X 8年前發布 | 37K 次閱讀 MyBatis 源碼分析 MyBatis3 持久層框架

來自: http://renchx.com/mybatis2

解析 typeAliases

在 XMLConfigBuilder 當中的 parseConfiguration 方法當中 typeAliasesElement(root.evalNode("typeAliases")); 來解析 typeAliases,下面是詳細代碼:

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

對應的 xml 格式如下:

<typeAliases>
        <package name="com.rcx.test"/>
        <typeAlias type="com.rcx.User" alias="User"/>
        <typeAlias type="com.rcx.Book" />
</typeAliases>

當是 package 的時候把這個包下面的所有類都掃描出來當成別名。下面的兩種方式是指定具體的 class,區別就是指定了別名,和沒指定別名,沒指定別名的時候使用 class.getSimpleName(); 來當作別名。

下面看一下 TypeAliasRegistry 的內部數據結構:

public class TypeAliasRegistry {

private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

public TypeAliasRegistry() { registerAlias("string", String.class); ...省略了內置的基本類似別名 registerAlias("iterator", Iterator.class);

}

}</code></pre>

結構很簡單,里面就是一個 HashMap 然后對這個 map 進行各種操作。

BaseBuilder

上面提到的 XMLConfigBuilder 繼承于 BaseBuilder,而且其他所有解析 XML 的 Builder 都會繼承這個抽象類。

看下 BaseBuilder 當中的結構:

public abstract class BaseBuilder {
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); }

...其他方法先省略 }</code></pre>

當創建 BaseBuilder 子類實例的時候,需要傳入 Configuration 的實例,入口的解析器 Builder 就是 XMLConfigBuilder,看下它的構造方法:

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

因為在 SqlSessionFactoryBuilder 類當中 build 生成 SqlSessionFactory 的時候是調用了 XMLConfigBuilder 的 parse 方法生成 Configuration 實例,代碼如下:

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        //省略了 try 和 catch
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
  }

public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }</code></pre>

所以初始化 Configuration 實例的流程就清晰了:

  1. 先創建 XMLConfigBuilder 實例,然后創建了 Configuration 實例
  2. 在 XMLConfigBuilder 當中把 Configuration 的每一個節點都解析然后設置進去
  3. 最后返回 Configuration 實例,完成初始化工作

當然 BaseBuilder 當中還包含了解析 Builder 的一些通用的工具方法,下面簡單列幾個:

  protected Pattern parseExpression(String regex, String defaultValue) {
    return Pattern.compile(regex == null ? defaultValue : regex);
  }

protected Boolean booleanValueOf(String value, Boolean defaultValue) { return value == null ? defaultValue : Boolean.valueOf(value); }

protected Integer integerValueOf(String value, Integer defaultValue) { return value == null ? defaultValue : Integer.valueOf(value); }</code></pre>

XMLConfigBuilder 的具體工作

要想了解 XMLConfigBuilder 的具體工作就要看它的 parseConfiguration 方法:

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));//解析內置的屬性
      typeAliasesElement(root.evalNode("typeAliases"));//解析別名
      pluginElement(root.evalNode("plugins"));//解析插件
      objectFactoryElement(root.evalNode("objectFactory"));//解析對象工廠
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectionFactoryElement(root.evalNode("reflectionFactory"));
      settingsElement(root.evalNode("settings"));//解析默認設置
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));//解析環境
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));//解析類型處理器
      mapperElement(root.evalNode("mappers"));//解析用戶定義的 mappers
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

從上面可以看出來,主要是對 config.xml 進行解析,剛才分析了別名的解析比較簡單,比較復雜的是 mappers 的解析。

下面挑一些進行簡單分析 解析插件

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

對應的 xml 如下:

<plugins>
        <plugin interceptor="com.rcx.TestInterceptor">
            <property name="" value=""/>
            <property name="" value=""/>
        </plugin>
</plugins>

不需要過多的解釋,主要看 configuration.addInterceptor(interceptorInstance); 在 configuration 當中的數據結構。

protected final InterceptorChain interceptorChain = new InterceptorChain();

public void addInterceptor(Interceptor interceptor) { interceptorChain.addInterceptor(interceptor); }

public class InterceptorChain {

private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }

public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); }

public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); }

}</code></pre>

數據結構也很明了,在 List 當中按順序存放所有的插件。

* 解析 typeHandler *

private void typeHandlerElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

對應的 XML 如下:

<typeHandlers>
        <package name="com.rcx.test"/>
        <typeHandler handler="" javaType="" jdbcType=""/>
</typeHandlers>

無論是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,還是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。

package 是在這個包下的都掃描成 typeHandler,typeHandler 標簽的具體分析,下面看 typeHandlerRegistry 的數據結構:

// 在 Configuration 當中的屬性
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

public final class TypeHandlerRegistry {

private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class); private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>(); private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this); private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();

public TypeHandlerRegistry() { register(Boolean.class, new BooleanTypeHandler()); ...//省略 register(char.class, new CharacterTypeHandler()); }

}</code></pre>

JDBC_TYPE_HANDLER_MAP 這個 map 存放的是 JDBC 類型的 TypeHandler 在 TypeHandlerRegistry 構造方法里面已經進行了初始化:

public TypeHandlerRegistry() {
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());
    。。。省略
}

之所以創建的是 EnumMap 是因為數據庫類型一定小于 64 種,使用 EnumMap 會更快、也節省空間。

TYPE_HANDLER_MAP 最個 map 是存放我們自定義的類型的轉換的 map,我們自定義的 TypeHandler 都要繼承于 BaseTypeHandler 這個類,BaseTypeHandler 繼承于 TypeReference,繼承于 TypeReference 的主要作用就是可通過反射拿到具體的 TypeHandler 的泛型 class。

public abstract class TypeReference<T> {

private final Type rawType;

protected TypeReference() { rawType = getSuperclassTypeParameter(getClass()); }

Type getSuperclassTypeParameter(Class<?> clazz) { Type genericSuperclass = clazz.getGenericSuperclass(); if (genericSuperclass instanceof Class) { // try to climb up the hierarchy until meet something useful if (TypeReference.class != genericSuperclass) { return getSuperclassTypeParameter(clazz.getSuperclass()); }

  throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
    + "Remove the extension or add a type parameter to it.");
}

Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
// TODO remove this when Reflector is fixed to return Types
if (rawType instanceof ParameterizedType) {
  rawType = ((ParameterizedType) rawType).getRawType();
}

return rawType;

}

public final Type getRawType() { return rawType; }

@Override public String toString() { return rawType.toString(); }

}</code></pre>

所以 TYPE_HANDLER_MAP 的 key 是 Type 類型,但是他的 value 是一個 Map<JdbcType, TypeHandler<?>> map,看起來很奇怪但是仔細想想很合理,有多個 jdbc 類型可以轉換成相同的 java 類型,那么有可能就需要不同的 TypeHandler。

ALL_TYPE_HANDLERS_MAP 就是存放所有的 typeHandler

TypeHandlerRegistry 類當中的重載的 register 就不仔細分析了。

---EOF---

</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></article>

 本文由用戶 Nel92X 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!