[MyBatis Source Analysis] Load the Xml configuration file

Keywords: PHP Apache xml Mybatis JDBC

Debug Entry

Find the XmlConfigBuilder Test in the mybatis source code and run shouldSuccessfully Load XMLConfigFile () in debug mode.

Initialization

First, create the XMLConfigBuilder object, in the construction method:

  • Reading files through InputStream to generate Document objects for XML, the operations related to XML are beyond the scope of this article, so we skip them.

  • Create a Configuration object that stores various configuration information and registers mappings of alias information within the construction method, such as JDBC for key and JdbcTransactionFactory for value.

  • Get ErrorContext through ThreadLocal, which is used to save context information and facilitate getting all kinds of information when throwing exceptions.

Analytical Configuration

Then call XMLConfigBuilder.parse() to parse the xml file:

  • XMLConfigBuilder.parse(), which uses global variable markers to avoid overloading configuration, returns the configuration object.

    • XMLConfigBuilder.parse().parseConfiguration() parses the sub-nodes of < configuration > in the configuration file, requiring that each sub-node cannot be repeated, and if repeated, an exception will be thrown:

      The content of the element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".

      • Resolve the < Properties > node, read the properties file specified by the node, and generate the Properties object

      • Resolve the < Settings > node and convert it into a Properties object, which is a HashTable. Next, check the key of the Properties object to see if it is a member variable of the Configuration class. The way to check is to store the name of the member variable that has the setter method into a collection, and judge by contains(), if not, it means that the < set > node has the wrong configuration, and then throw an exception.

        • Load < Settings > node-defined Vfs (virtual file system)

        • Loading < Settings > node-defined log implementation

      • Resolve < type Aliases > nodes and configure aliases. The child nodes can only be < package > or < type Alias > nodes.

        • If the child node is <typeAlias>, an alias-to-class mapping is established. If no alias is specified, the default alias class.getSimpleName() is used. If the Alias annotation is configured, the value of the annotation is used as the alias and the alias is lowercase.

        • If the child node is < package >, the alias is configured in units of the entire package

          • Get the subfile path set in the package directory

          • Traverse the entire collection, and if it's a file at the end of. class, load it with a class loader

            • Use the policy pattern to determine whether this class is a subclass of the Object class. If so, add this class to the result set?

          • Traversing the result set, if it is not an anonymous class, not an interface, and not a member class, establishes an alias-to-class object mapping. The default alias class.getSimpleName() is class. If the Alias annotation is configured, the value of the annotation is used as the alias, and the alias is lowercase.

      • Parse the < plugins > node, create the interceptor object by reflection, and add the object to the interceptor chain of the configuration object (an ArrayList)

      • Resolve the < object WrapperFactory > node and generate the object factory by reflection

      • Resolve < reflectorFactory > nodes, and generate reflector factories by reflection. The reflector caches information about class objects such as setter method set and getter method set.

      • Apply the configuration parsed by < set > node to the configuration object, call the setter method of each member variable of the configuration, and get the value by getOrDefault method of the properties object.

      • Resolve < environments > nodes, database link address, account password and other configurations, and < transactionManager > and < dataSource > nodes to generate data sources and transaction factory objects.

      • Resolve the "database IdProvider> node

      • Resolve the <type handlers> node, get the class object of TypeHandler by alias lookup cache or class.forName, and then reflect the generated object. The structure of the object is Map<JavaType, Map<JdbcType, TypeHandler<?>, and the mapping relationship is Java type->Jdbc type->TypeHandler. At the same time, it is stored in another cache with the structure of Map < Class <?> and TypeHandler <?>.

        • If the child node is < typeHandler >

          • First, find out if the class object has been cached according to the JavaType name, and then retrieve the class object by reflection and cache it if no cache has been hit.

          • Get the JdbcType object by enumerating. valueOf()

          • First, find out if the class object has been cached according to the name of the TypeHandler. If no cache has been hit, retrieve the class object by reflection and cache it.

          • Create mapping relationships for JavaType - > JdbcType - > TypeHandler objects, JdbcType is allowed to be empty, and JdbcType can be obtained from annotations

            • In which the object instance TypeHandler is constructed by reflection

          • Establishing Mapping Relations of TypeHandler. Class - > TypeHandler Objects

        • If the child node is < package >, get the TypeHandler as a whole package

          • Get the subfile path set in the package directory

          • Traverse the entire collection, and if it's a file at the end of. class, load it with a class loader

            • Use the policy pattern to determine if this class is a subclass of the TypeHandler class, and if so, add this class to the result set?

          • Traversing the result set, if it is not an anonymous inner class, an interface, or an abstract class, then a mapping of JavaType - > JdbcType - > TypeHandler is established based on MappedTypes annotations and MappedJdbcTypes annotations, where JdbcType is allowed to be empty

      • Parse < mappers > nodes, read xxxMapper.xml from disk to memory and parse it into related configuration objects, which are implemented in XML Mapper Builder and in another article

appendix

Typical mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--
​
       Copyright 2009-2016 the original author or authors.
​
       Licensed under the Apache License, Version 2.0 (the "License");
       you may not use this file except in compliance with the License.
       You may obtain a copy of the License at
​
          http://www.apache.org/licenses/LICENSE-2.0
​
       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       See the License for the specific language governing permissions and
       limitations under the License.
​
-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="org/apache/ibatis/databases/blog/blog-derby.properties"/><settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="false"/>
        <setting name="multipleResultSetsEnabled" value="true"/>
        <setting name="useColumnLabel" value="true"/>
        <setting name="useGeneratedKeys" value="false"/>
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <setting name="defaultStatementTimeout" value="25"/>
    </settings><typeAliases>
        <package name="org.apache.ibatis.domain"/>
    </typeAliases>
    <!--<typeAliases>-->
        <!--<typeAlias alias="Author" type="org.apache.ibatis.domain.blog.Author"/>-->
        <!--<typeAlias alias="Blog" type="org.apache.ibatis.domain.blog.Blog"/>-->
        <!--<typeAlias alias="Comment" type="org.apache.ibatis.domain.blog.Comment"/>-->
        <!--<typeAlias alias="Post" type="org.apache.ibatis.domain.blog.Post"/>-->
        <!--<typeAlias alias="Section" type="org.apache.ibatis.domain.blog.Section"/>-->
        <!--<typeAlias alias="Tag" type="org.apache.ibatis.domain.blog.Tag"/>-->
    <!--</typeAliases>-->
    <typeHandlers>
        <typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/>
    </typeHandlers><objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory">
        <property name="objectFactoryProperty" value="100"/>
    </objectFactory><plugins>
        <plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">
            <property name="pluginProperty" value="100"/>
        </plugin>
    </plugins><environments default="development">
        <environment id="development">
            <transactionManager type="JDBC">
                <property name="" value=""/>
            </transactionManager>
            <dataSource type="UNPOOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments><mappers>
        <mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>
        <mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/>
        <mapper resource="org/apache/ibatis/builder/CachedAuthorMapper.xml"/>
        <mapper resource="org/apache/ibatis/builder/PostMapper.xml"/>
        <mapper resource="org/apache/ibatis/builder/NestedBlogMapper.xml"/>
    </mappers></configuration>

Unit test method

This method is located in the XmlConfigBuilderTest class:

@Test
void shouldSuccessfullyLoadXMLConfigFile() throws Exception {
  // System.setProperty(XPathParser.KEY_USE_XSD, "true");
  String resource = "org/apache/ibatis/builder/xsd/CustomizedSettingsMapperConfig.xml";
  try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
    XMLConfigBuilder builder = new XMLConfigBuilder(inputStream);
    Configuration config = builder.parse();
​
    assertEquals(AutoMappingBehavior.NONE, config.getAutoMappingBehavior());
    assertEquals(AutoMappingUnknownColumnBehavior.WARNING, config.getAutoMappingUnknownColumnBehavior());
    assertFalse(config.isCacheEnabled());
    assertTrue(config.getProxyFactory() instanceof CglibProxyFactory);
    assertTrue(config.isLazyLoadingEnabled());
    assertTrue(config.isAggressiveLazyLoading());
    assertFalse(config.isMultipleResultSetsEnabled());
    assertFalse(config.isUseColumnLabel());
    assertTrue(config.isUseGeneratedKeys());
    assertEquals(ExecutorType.BATCH, config.getDefaultExecutorType());
    assertEquals(Integer.valueOf(10), config.getDefaultStatementTimeout());
    assertEquals(Integer.valueOf(100), config.getDefaultFetchSize());
    assertTrue(config.isMapUnderscoreToCamelCase());
    assertTrue(config.isSafeRowBoundsEnabled());
    assertEquals(LocalCacheScope.STATEMENT, config.getLocalCacheScope());
    assertEquals(JdbcType.NULL, config.getJdbcTypeForNull());
    assertEquals(new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString", "xxx")), config.getLazyLoadTriggerMethods());
    assertFalse(config.isSafeResultHandlerEnabled());
    assertTrue(config.getDefaultScriptingLanguageInstance() instanceof RawLanguageDriver);
    assertTrue(config.isCallSettersOnNulls());
    assertEquals("mybatis_", config.getLogPrefix());
    assertEquals(Slf4jImpl.class.getName(), config.getLogImpl().getName());
    assertEquals(JBoss6VFS.class.getName(), config.getVfsImpl().getName());
    assertEquals(String.class.getName(), config.getConfigurationFactory().getName());
​
    assertEquals(Author.class, config.getTypeAliasRegistry().getTypeAliases().get("blogauthor"));
    assertEquals(Blog.class, config.getTypeAliasRegistry().getTypeAliases().get("blog"));
    assertEquals(Cart.class, config.getTypeAliasRegistry().getTypeAliases().get("cart"));
​
    assertTrue(config.getTypeHandlerRegistry().getTypeHandler(Integer.class) instanceof CustomIntegerTypeHandler);
    assertTrue(config.getTypeHandlerRegistry().getTypeHandler(Long.class) instanceof CustomLongTypeHandler);
    assertTrue(config.getTypeHandlerRegistry().getTypeHandler(String.class) instanceof CustomStringTypeHandler);
    assertTrue(config.getTypeHandlerRegistry().getTypeHandler(String.class, JdbcType.VARCHAR) instanceof CustomStringTypeHandler);
​
    ExampleObjectFactory objectFactory = (ExampleObjectFactory)config.getObjectFactory();
    assertEquals(1, objectFactory.getProperties().size());
    assertEquals("100", objectFactory.getProperties().getProperty("objectFactoryProperty"));
​
    assertTrue(config.getObjectWrapperFactory() instanceof CustomObjectWrapperFactory);
​
    assertTrue(config.getReflectorFactory() instanceof CustomReflectorFactory);
​
    ExamplePlugin plugin = (ExamplePlugin)config.getInterceptors().get(0);
    assertEquals(1, plugin.getProperties().size());
    assertEquals("100", plugin.getProperties().getProperty("pluginProperty"));
​
    Environment environment = config.getEnvironment();
    assertEquals("development", environment.getId());
    assertTrue(environment.getDataSource() instanceof UnpooledDataSource);
    assertTrue(environment.getTransactionFactory() instanceof JdbcTransactionFactory);
​
    assertEquals("derby", config.getDatabaseId());
​
    assertEquals(4, config.getMapperRegistry().getMappers().size());
    assertTrue(config.getMapperRegistry().hasMapper(CachedAuthorMapper.class));
    assertTrue(config.getMapperRegistry().hasMapper(CustomMapper.class));
    assertTrue(config.getMapperRegistry().hasMapper(BlogMapper.class));
    assertTrue(config.getMapperRegistry().hasMapper(NestedBlogMapper.class));
  } finally {
    // System.clearProperty(XPathParser.KEY_USE_XSD);
  }
}

A series of default aliases

Basic types

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

Other types

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

Posted by Imad on Wed, 05 Jun 2019 12:06:24 -0700