mybatis源码分析:XMLConfigBuilder解析过程

Scroll Down

1、概述

XMLConfigBuilder主要的作用是从myabtis XML文件中解析相关数据到Configuration中。

2、XMLConfigBuilder解析过程

在XMLConfigBuilder内部解析XML文件的操作实际上就委托给XPathParser这个工具类来完成的。
parse()方法代码如下:

public Configuration parse() {
    // 对于一个Mybatis运行时配置文件只能解析一次
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 使用XPathParser解析XML文件中以/configuration为根结点的内容
    // 调用parseConfiguration构造Configuration
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

parseConfiguration方法代码如下:

private void parseConfiguration(XNode root) {
    try {
      // 解析properties节点
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      // 解析settings节点
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      // 将vfsImpl属性设置到Configuration中
      loadCustomVfs(settings);
      // 将logImpl属性设置到Configuration中
      loadCustomLogImpl(settings);
      // 解析typeAliases节点
      typeAliasesElement(root.evalNode("typeAliases"));
      // 解析plugins节点
      pluginElement(root.evalNode("plugins"));
      // 解析objectFactory节点
      objectFactoryElement(root.evalNode("objectFactory"));
      // 解析objectWrapperFactory节点
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      // 解析reflectorFactory节点
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // 解析environments节点
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      // 解析databaseIdProvider节点
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // 解析typeHandlers节点
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 解析mappers节点
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

3、解析properties节点

propertiesElement方法代码如下:

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      // 获取所有子标签下的name和value属性,并构造成Properties。
      Properties defaults = context.getChildrenAsProperties();
      // 获取properties标签的resource属性,比如<properties resource="***/***.properties">
      String resource = context.getStringAttribute("resource");
      // 获取properties标签的url属性
      String url = context.getStringAttribute("url");
      // 如果两个属性都存在的情况下是不允许的,抛出BuilderException异常
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      // 如果resource属性不为空,则直接解析resource路径下的properties文件
      // 并将Properties属性合并到一起
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      // 如果url属性不为空,则解析URL指向的Properties文件内容
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      Properties vars = configuration.getVariables();
      // 如果Configuration的variables字段不为空,则补充这些属性
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      // 将从XML文件中解析到的内容设置到Configuration中
      configuration.setVariables(defaults);
    }
}

4、解析settings节点

private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    // 解析settings标签下的全部property标签
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    // 检查所有property的name是否都能在Configuration类中找到对应的setter方法
    for (Object key : props.keySet()) {
      // 如果没有对应的方法,那么就抛出BuilderException异常
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    // 返回解析到的properties
    return props;
}

5、解析typeAliases节点

// 解析类型别名
private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        // 如果子标签是package
        if ("package".equals(child.getName())) {
          // 获取package标签的name属性,也就是包名
          String typeAliasPackage = child.getStringAttribute("name");
          // 这是使用的是TypeAliasRegistry来解析注册包名下全部的类型具体逻辑下面会详细说明
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          // 如果是typeAlias标签,那么就获取alias及type属性
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            // 获取type的Class类
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
              // 将类型注册到typeAliasRegistry中
              typeAliasRegistry.registerAlias(clazz);
            } else {
              // 将类型和别名注册到typeAliasRegistry中
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
}

这个方法中用到了TypeAliasRegistry,这里详细介绍下它的具体注册逻辑。
这个类在被创建的时候就注册了一些别名,代码如下:

public TypeAliasRegistry() {
    registerAlias("string", String.class);
    
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);
    
    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);
    
    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);
    
    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);
    
    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);
    
    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);
    
    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);
    
    registerAlias("ResultSet", ResultSet.class);
}

registerAliases()注册包名的方法逻辑如下:

public void registerAliases(String packageName) {
    registerAliases(packageName, Object.class);
}

public void registerAliases(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    // 解析包名路径下全部的类型
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for (Class<?> type : typeSet) {
      // Ignore inner classes and interfaces (including package-info.java)
      // 如果是内部类或者接口则跳过
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        // 调用registerAlias注册类型到typeAliases中
        registerAlias(type);
      }
    }
}

registerAlias方法代码如下:

public void registerAlias(Class<?> type) {
    // 以类型的名称作为别名(不包含包名的类名)
    String alias = type.getSimpleName();
    // 判断该类型是被@Alias注解标记,如果存在注解则取注解的值。
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    }
    // 调用registerAlias的重载方法,将别名和类型注册到typeAliases中
    registerAlias(alias, type);
}

// 注册alias和value到typeAliases中
public void registerAlias(String alias, Class<?> value) {
    // 如果此时不存在别名,那么会抛出异常
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // 将别名首字母变成小写
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    // 如果已经存在,那么就抛出异常
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    // 注册别名和类型信息
    typeAliases.put(key, value);
}

6、解析plugins节点

mybatis的插件是这个时候被注册的。
举个例子:

<plugins>
    <plugin interceptor="org.mybatis.example.ExamplePlugin">
        <property name="someProperty" value="100"/>
    </plugin>
</plugins>

代码如下:

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      // 遍历plugins标签的子节点
      for (XNode child : parent.getChildren()) {
        // 获取子节点的interceptor的属性,该属性为类型的完全限定名
        String interceptor = child.getStringAttribute("interceptor");
        // 获取这个interceptor的全部属性,并封装成Properties
        Properties properties = child.getChildrenAsProperties();
        // 构建这个interceptor的实例。
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
        // 为这个interceptor的实例设置属性
        interceptorInstance.setProperties(properties);
        // 将interceptor属性设置到Configuration中
        configuration.addInterceptor(interceptorInstance);
      }
    }
}

这里使用了resolveClass来解析类型的Class对象,代码如下:

protected <T> Class<? extends T> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      // 调用resolveAlias来解析alias的类型
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
}

protected <T> Class<? extends T> resolveAlias(String alias) {
  // 调用别名管理器的resolveAlias的方法解析别名为alias
  return typeAliasRegistry.resolveAlias(alias);
}

TypeAliasRegistry中的resolveAlias方法如下

public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // 首先尝试从typeAlias中获取类型,如果不存在,则直接解析类名。
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
}

7、解析objectFactory节点

在 MyBatis 中,默认的 ObjectFactory 要做的就是实例化查询结果对应的目标类,有两种方式可以将查询结果的值映射到对应的目标类,一种是通过目标类的默认构造方法,另外一种就是通过目标类的有参构造方法。

解析代码如下:

private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      // 获取objectFactory的type属性
      String type = context.getStringAttribute("type");
      // 获取objectFactory的properties属性
      Properties properties = context.getChildrenAsProperties();
      // 实例化ObjectFactory,逻辑与实例化plugin一致
      ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      // 将属性设置到ObjectFactory中
      factory.setProperties(properties);
      // 将ObjectFactory对象设置到Configuration属性中
      configuration.setObjectFactory(factory);
    }
}

8、解析objectWrapperFactory节点

实际用途待补充
解析代码如下:

private void objectWrapperFactoryElement(XNode context) throws Exception {
    if (context != null) {
      // 获取objectWrapperFactory标签的type属性
      String type = context.getStringAttribute("type");
      // 实例化objectWrapperFactory对象
      ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      // 将objectWrapperFactory对象设置到Configuration中
      configuration.setObjectWrapperFactory(factory);
    }
}

9、解析reflectorFactory节点

允许用户自定义反射工具类

private void reflectorFactoryElement(XNode context) throws Exception {
    if (context != null) {
      // 获取reflectorFactory节点的type属性
      String type = context.getStringAttribute("type");
      // 其余操作同上
      ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      configuration.setReflectorFactory(factory);
    }
}

10、解析environments节点

举个例子:

<environments default="dev">
    <environment id="dev">
        <transactionManager type="JDBC">
            <property name="" value=""/>
        </transactionManager>
        <dataSource type="UNPOOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://*.*.*.8:3306/mybatis"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

解析方法如下:

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        // 首先获取default属性,指定环境
        environment = context.getStringAttribute("default");
      }
      // 解析resources标签下的所有子节点
      for (XNode child : context.getChildren()) {
        // 获取resource的ID
        String id = child.getStringAttribute("id");
        // 判断当前默认设置的ID是否是该子节点的ID,如果不是则不解析
        if (isSpecifiedEnvironment(id)) {
          // 解析事务管理器
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          // 解析连接池工厂类
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          // 获取连接池
          DataSource dataSource = dsFactory.getDataSource();
          // 使用构造器模式创建Environment对象
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
}

事务管理器的解析代码如下:

// 解析事务管理器和数据库连接池工厂类的逻辑与之前一致,不再详细分析
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
}

特殊需要注意的是。这里的dataSource的type指定的是UNPOOLED,按照逻辑变成小写是unpooled这个类型是事先在Configuration中定义好的别名。Configuration对象是在XMLConfigBuilder实现创建好的。
代码如下:

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    
    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    
    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    
    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    
    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    
    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
}

11、解析databaseIdProvider节点

解析代码如下:

private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    if (context != null) {
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      if ("VENDOR".equals(type)) {
        type = "DB_VENDOR";
      }
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      configuration.setDatabaseId(databaseId);
    }
}

原理与之前的一致,不再赘述。

12、解析typeHandlers节点

用户注册自定义的类型转换器
解析代码如下;

private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        // 存在package节点
        if ("package".equals(child.getName())) {
          // 获取节点name属性
          String typeHandlerPackage = child.getStringAttribute("name");
          // 解析并注册typeHandler
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          // 获取子节点的javaType属性
          String javaTypeName = child.getStringAttribute("javaType");
          // 获取子节点的jdbcType属性
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          // 获取子节点的handler属性
          String handlerTypeName = child.getStringAttribute("handler");
          // 解析类型名
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          // 调用typeHandlerRegistry的register方法注册Type Handler实例。
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
}

需要注意:mybatis框架中默认注册了一些类型转换器:

public TypeHandlerRegistry(Configuration configuration) {
    this.unknownTypeHandler = new UnknownTypeHandler(configuration);
    
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());
    
    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());
    
    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());
    
    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());
    
    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());
    
    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());
    
    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());
    
    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());
    
    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());
    
    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());
    
    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
    
    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());
    
    register(Object.class, unknownTypeHandler);
    register(Object.class, JdbcType.OTHER, unknownTypeHandler);
    register(JdbcType.OTHER, unknownTypeHandler);
    
    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());
    
    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
    
    register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
    
    register(Instant.class, new InstantTypeHandler());
    register(LocalDateTime.class, new LocalDateTimeTypeHandler());
    register(LocalDate.class, new LocalDateTypeHandler());
    register(LocalTime.class, new LocalTimeTypeHandler());
    register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
    register(OffsetTime.class, new OffsetTimeTypeHandler());
    register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
    register(Month.class, new MonthTypeHandler());
    register(Year.class, new YearTypeHandler());
    register(YearMonth.class, new YearMonthTypeHandler());
    register(JapaneseDate.class, new JapaneseDateTypeHandler());
    
    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
}

TypeHandlerRegistry的register代码如下:

public void register(Class<?> typeHandlerClass) {
    boolean mappedTypeFound = false;
    // 获取typeHandler类型的MappedTypes注解。
    MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
    if (mappedTypes != null) {
      for (Class<?> javaTypeClass : mappedTypes.value()) {
        // 调用register方法注册
        register(javaTypeClass, typeHandlerClass);
        mappedTypeFound = true;
      }
    }
    if (!mappedTypeFound) {
      register(getInstance(null, typeHandlerClass));
    }
}

register重载方法如下:

public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
    register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}

public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
    register((Type) javaType, typeHandler);
}

private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
    if (mappedJdbcTypes != null) {
      for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
        register(javaType, handledJdbcType, typeHandler);
      }
      if (mappedJdbcTypes.includeNullJdbcType()) {
        register(javaType, null, typeHandler);
      }
    } else {
      register(javaType, null, typeHandler);
    }
}

private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
      if (map == null || map == NULL_TYPE_HANDLER_MAP) {
        map = new HashMap<>();
      }
      // 执行注册
      map.put(jdbcType, handler);
      typeHandlerMap.put(javaType, map);
    }
    allTypeHandlersMap.put(handler.getClass(), handler);
}

13、解析mappers节点

解析代码如下:

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        // 如果标签名为package
        if ("package".equals(child.getName())) {
          // 获取标签name属性
          String mapperPackage = child.getStringAttribute("name");
          // 调用configuration的addMapper方法注册mapper
          configuration.addMappers(mapperPackage);
        } else {
          // 获取resource属性
          String resource = child.getStringAttribute("resource");
          // 获取url属性
          String url = child.getStringAttribute("url");
          // 获取class属性
          String mapperClass = child.getStringAttribute("class");
          // 如果resource属性存在
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 使用XMLMapperBuilder解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {  // 如果url属性存在
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {  // 如果class属性存在
            // 获取Class对象
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            注册mapper
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
}

至此关于Configuration的全部属性已经全部解析完成。
整体的解析业务图如下所示:
mybatisConfigurationBuild.png

整体的思想就是创建一Configuration对象,然后解析XML文件,将从XML文件中解析到的数据封装成对应的数据结构,设置到Configuration对象中供后续运行时使用。