当前位置 : 主页 > 网页制作 > xml >

【MyBatis源码分析】加载Xml配置文件

来源:互联网 收集:自由互联 发布时间:2021-06-13
Debug入口 在mybatis源码中找到XmlConfigBuilderTest,以debug模式运行shouldSuccessfullyLoadXMLConfigFile()。 初始化 首先创建XMLConfigBuilder对象,在构造方法中: 通过InputStream读取文件,生成XML的Document对

Debug入口

在mybatis源码中找到XmlConfigBuilderTest,以debug模式运行shouldSuccessfullyLoadXMLConfigFile()。

初始化

首先创建XMLConfigBuilder对象,在构造方法中:

  • 通过InputStream读取文件,生成XML的Document对象,XML相关的操作不属于本文研究范围,因此跳过

  • 创建Configuration对象,该对象存储各种配置信息,构造方法内注册一些别名信息的映射,例如key是JDBC,value是JdbcTransactionFactory.class

  • 通过ThreadLocal获取ErrorContext,用于保存上下文信息,方便抛异常的时候获得各种信息

解析配置

接着调用XMLConfigBuilder.parse()解析xml文件:

  • XMLConfigBuilder.parse(),使用全局变量标记避免重复加载配置,返回配置对象(Configuration)

    • XMLConfigBuilder.parse().parseConfiguration(),解析配置文件内<configuration>的各个子节点,要求各个子节点不能重复,若重复会抛出异常:

      元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"

      • 解析<properties>节点,读取该节点指定的properties文件并生成Properties对象

      • 解析<settings>节点,转换成Properties对象,它是一个HashTable。接着对Properties对象的key做检查,看看是否是Configuration类的的成员变量。检查的方法是把拥有setter方法的成员变量名,存储到一个集合中,通过contains()判断,如果不包含,则意味着<setting>节点有错误的配置,于是抛出异常

        • 加载<settings>节点自定义的Vfs(虚拟文件系统))

        • 加载<settings>节点自定义的日志实现

      • 解析<typeAliases>节点,配置别名。子节点只能是<package>或者<typeAlias>节点。

        • 如果子节点是<typeAlias>,则建立别名到class对象的映射。如果没有指定别名,默认别名class.getSimpleName(),如果配置了Alias注解,则使用注解的值作为别名,别名使用小写。

        • 如果子节点是<package>,则以整个包为单位配置别名

          • 获取包目录下的子文件路径集合

          • 遍历整个集合,如果是.class结尾的文件,则用类加载器加载它

            • 使用策略模式,判断这个类是不是Object类的子类,是的话将这个类对象加入结果集合

          • 遍历结果集合,如果不是匿名类且不是接口且不是成员类则建立别名到class对象的映射,默认别名class.getSimpleName(),如果配置了Alias注解,则使用注解的值作为别名,别名使用小写。

      • 解析<plugins>节点,通过反射创建拦截器对象,将该对象添加到configuration对象的拦截器链(一个ArrayList)中

      • 解析<objectWrapperFactory>节点,通过反射,生成对象工厂

      • 解析<reflectorFactory>节点,通过反射生成反射器工厂,反射器缓存了类对象的相关信息例如setter方法集合和getter方法集合

      • <setting>节点解析出来的配置应用到configuration对象中,调用configuration每个成员变量的setter方法,以及properties对象的getOrDefault方法获取值

      • 解析<environments>节点,解析数据库链接地址、账号密码等配置,解析<transactionManager><dataSource>节点,生成数据源和事务工厂对象

      • 解析<<databaseIdProvider>>节点

      • 解析<typeHandlers>节点,通过别名查找缓存或者class.forName获得TypeHandler的class对象,再反射生成对象,将该对象缓存起来,结构为Map<JavaType,Map<JdbcType, TypeHandler<?>>>, 映射的关系为Java类型->Jdbc类型->TypeHandler。同时存到另外一份缓存中,结构为Map<Class<?>, TypeHandler<?>>。

        • 如果子节点是<typeHandler>

          • 先根据JavaType名称查找是否已经缓存了class对象,如果没有命中缓存则通过反射获取class对象并缓存起来

          • 通过枚举.valueOf()获取JdbcType对象

          • 先根据TypeHandler名称查找是否已经缓存了class对象,如果没有命中缓存则通过反射获取class对象并缓存起来

          • 建立JavaType->JdbcType->TypeHandler对象的映射关系,JdbcType允许为空,且JdbcType可以从注解中获取

            • 其中通过反射构造对象实例TypeHandler

          • 建立TypeHandler.class->TypeHandler对象的映射关系

        • 如果子节点是<package>,则以整个包为单位获取TypeHandler

          • 获取包目录下的子文件路径集合

          • 遍历整个集合,如果是.class结尾的文件,则用类加载器加载它

            • 使用策略模式,判断这个类是不是TypeHandler类的子类,是的话将这个类对象加入结果集合

          • 遍历结果集合,如果不是匿名内部类、不是接口、不是抽象类,则根据MappedTypes注解、MappedJdbcTypes注解建立JavaType->JdbcType->TypeHandler的映射,其中JdbcType允许为空

      • 解析<mappers>节点,从磁盘读取xxxMapper.xml到内存并解析成相关的配置对象,具体实现在XMLMapperBuilder中,在另一篇文章

附录

典型的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>
View Code

单元测试方法

该方法位于XmlConfigBuilderTest类中:

@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);
  }
}
View Code

一系列默认别名

基本类型

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

其他类型

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);
}
View Code
网友评论