天天看點

MyBatis源碼解析 ——MyBatis初始化過程解析MyBatis初始化過程

创建项目

为了看清楚MyBatis的整个初始化过程,先创建一个简单的Java项目,目录结构如下图所示:

MyBatis源碼解析 ——MyBatis初始化過程解析MyBatis初始化過程

以下代码均有代码生成器生成

Student实体类

package com.softtool.mybatis.dao;

import java.io.Serializable;

public class Student implements Serializable {
    private Integer id;

    private String name;

    private Integer age;

    private String sex;

    private static final long serialVersionUID = 1L;

    public Student(Integer id, String name, Integer age, String sex) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Student() {
        super();
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + "]";
	}
    
}
           

StudentMapper ç±»

package com.softtool.mybatis.dao;

public interface StudentMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(Student record);

    int insertSelective(Student record);


    Student selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Student record);

    int updateByPrimaryKey(Student record);
}
           

配置文件xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.softtool.mybatis.dao.StudentMapper">
  <resultMap id="BaseResultMap" type="com.softtool.mybatis.dao.Student">
    <constructor>
      <idArg column="id" javaType="java.lang.Integer" jdbcType="INTEGER" />
      <arg column="name" javaType="java.lang.String" jdbcType="VARCHAR" />
      <arg column="age" javaType="java.lang.Integer" jdbcType="INTEGER" />
      <arg column="sex" javaType="java.lang.String" jdbcType="VARCHAR" />
    </constructor>
  </resultMap>
  <sql id="Base_Column_List">
    id, name, age, sex
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from student
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from student
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.softtool.mybatis.dao.Student">
    insert into student (id, name, age, 
      sex)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, 
      #{sex,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="com.softtool.mybatis.dao.Student">
    insert into student
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="name != null">
        name,
      </if>
      <if test="age != null">
        age,
      </if>
      <if test="sex != null">
        sex,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="age != null">
        #{age,jdbcType=INTEGER},
      </if>
      <if test="sex != null">
        #{sex,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.softtool.mybatis.dao.Student">
    update student
    <set>
      <if test="name != null">
        name = #{name,jdbcType=VARCHAR},
      </if>
      <if test="age != null">
        age = #{age,jdbcType=INTEGER},
      </if>
      <if test="sex != null">
        sex = #{sex,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.softtool.mybatis.dao.Student">
    update student
    set name = #{name,jdbcType=VARCHAR},
      age = #{age,jdbcType=INTEGER},
      sex = #{sex,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>
           

mybatis.xml MyBatis的配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="com/softtool/mybatis/dao/StudentMapper.xml"/>
  </mappers>
</configuration>
           

pom.xml

项目是maven项目,配置的pom如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>test002</groupId>
    <artifactId>test002</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>

        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>


</project>
           

Main 主函数

package com.softtool.mybatis;


import com.softtool.mybatis.dao.Student;
import com.softtool.mybatis.dao.StudentMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.reflection.Reflector;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Constructor;

/**
 * Hello world!
 *
 */
public class App

{
    public static void main( String[] args ) throws IOException
    {
        Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
       
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsReader);
        SqlSession session = sqlSessionFactory.openSession();

        StudentMapper mapper = session.getMapper(StudentMapper.class);
        Student student = mapper.selectByPrimaryKey(1);

    }
}

           

创建的sql

-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `name` varchar(255) NOT NULL COMMENT '姓名',
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `sex` varchar(255) DEFAULT NULL COMMENT '性别',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=latin1;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('ss', '1', '1', 'mm');
INSERT INTO `student` VALUES ('ddd', '2', '2', 'women');
INSERT INTO `student` VALUES ('name1', '3', '2', 'nan');
INSERT INTO `student` VALUES ('222', '4', '31', 'man');
           

运行结果

Student [id=1, name=ss, age=1, sex=mm]
           

MyBatis初始化过程

获取配置文件

当系统初始化时,首先会读取配置文件,

Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
           

创建SqlSessionFactoryBuilder对象

从SqlSessionFactoryBuilder的名字中可以看出,SqlSessionFactoryBuilder是用来创建SqlSessionFactory对象的。 来看一下SqlSessionFactoryBuilder源码:,SqlSessionFactoryBuilder中只有一些重载的build函数,这些build函数的入参都是MyBatis配置文件的输入流,返回值都是SqlSessionFactory;由此可见,SqlSessionFactoryBuilder的作用很纯粹,就是用来通过配置文件创建SqlSessionFactory对象的。

MyBatis源碼解析 ——MyBatis初始化過程解析MyBatis初始化過程

SqlSessionFactory 创建过程

下面具体来看一下,build函数是如何创建SqlSessionFactory对象的。

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
   try {
     XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
     return build(parser.parse());
   } catch (Exception e) {
     throw ExceptionFactory.wrapException("Error building SqlSession.", e);
   } finally {
     ErrorContext.instance().reset();
     try {
       reader.close();
     } catch (IOException e) {
       // Intentionally ignore. Prefer previous error.
     }
   }
 }
           

构造XMLConfigBuilder对象

build函数首先会构造一个XMLConfigBuilder对象,从名字上大致可以猜到,该对象是用来解析XML配置文件的。下面来看一下XMLConfigBuilder的体系结构。

MyBatis源碼解析 ——MyBatis初始化過程解析MyBatis初始化過程
  • XMLxxxBuilder是用来解析XML配置文件的,不同类型XMLxxxBuilder用来解析MyBatis配置文件的不同部位。比如:XMLConfigBuilder用来解析MyBatis的配置文件,XMLMapperBuilder用来解析MyBatis中的映射文件(如上文提到的ProductMapper.xml),XMLStatementBuilder用来解析映射文件中的SQL语句。
  • 这些XMLxxxBuilder都有一个共同的父类——BaseBuilder。这个父类维护了一个全局的Configuration对象,MyBatis的配置文件解析后就以Configuration对象的形式存储。
  • 当创建XMLConfigBuilder对象时,就会初始化Configuration对象,并且在初始化Configuration对象的时候,一些别名会被注册到Configuration的typeAliasRegistry容器中。
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;
}
           
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);
  }
           

解析配置文件

当有了XMLConfigBuilder对象之后,接下来就可以用它来解析配置文件了

private void parseConfiguration(XNode root) {
   try {
     propertiesElement(root.evalNode("properties")); //issue #117 read properties first
     typeAliasesElement(root.evalNode("typeAliases"));
     pluginElement(root.evalNode("plugins"));
     objectFactoryElement(root.evalNode("objectFactory"));
     objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
     settingsElement(root.evalNode("settings"));
     environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
     databaseIdProviderElement(root.evalNode("databaseIdProvider"));
     typeHandlerElement(root.evalNode("typeHandlers"));
     mapperElement(root.evalNode("mappers"));
   } catch (Exception e) {
     throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
   }
 }

           

从上述代码中可以看到,XMLConfigBuilder会依次解析配置文件中的<properties>、< settings >、< environments>、< typeAliases >、< plugins >、< mappers >等属性。下面介绍下几个重要属性的解析过程。

节点的解析过程

节点:

<property name="driver" value="com.mysql.jdbc.Driver"/>
       <property name="url" value="jdbc:mysql://localhost:3306/test"/>
       <property name="username" value="root"/>
       <property name="password" value=""/>
           
  • 节点的解析过程:
private void propertiesElement(XNode context) throws Exception {
   if (context != null) {
     Properties defaults = context.getChildrenAsProperties();
     String resource = context.getStringAttribute("resource");
     String url = context.getStringAttribute("url");
     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.");
     }
     if (resource != null) {
       defaults.putAll(Resources.getResourceAsProperties(resource));
     } else if (url != null) {
       defaults.putAll(Resources.getUrlAsProperties(url));
     }
     Properties vars = configuration.getVariables();
     if (vars != null) {
       defaults.putAll(vars);
     }
     parser.setVariables(defaults);
     configuration.setVariables(defaults);
   }
 }

           
  • 首先读取<resources>节点下的所有<resource>节点,并将每个节点的name和value属性存入Properties中。
  • 然后读取<resources>节点上的resource、url属性,并获取指定配置文件中的name和value,也存入Properties中。(PS:由此可知,如果resource节点上定义的属性和properties文件中的属性重名,那么properties文件中的属性值会覆盖resource节点上定义的属性值。)
  • 最终,携带所有属性的Properties对象会被存储在Configuration对象中。

settings节点的解析过程

settings 节点的解析过程: <settings>属性的解析过程和 <properties>属性的解析过程极为类似,这里不再赘述。最终,所有的setting属性都被存储在Configuration对象中。

<settings>
    <setting name="cacheEnabled" value="true"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
	</settings>
           

mappers节点的解析过程

<mappers>节点的定义方式有如下四种: 方式1:

<mappers>
<package name="org.mybatis.builder"/>
</mappers> 
           

方式2:

<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers> 
方式3:

           
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers> 

           

方式4:

<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>

           

<mappers>节点的解析过程如下:

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            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<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

           

MyBatis会遍历<mappers>下所有的子节点,如果当前遍历到的节点是<package>,则MyBatis会将该包下的所有Mapper Class注册到configuration的mapperRegistry容器中。 如果当前节点为<mapper>,则会依次获取resource、url、class属性,解析映射文件,并将映射文件对应的Mapper Class注册到configuration的mapperRegistry容器中。

其中,<mapper>节点的解析过程如下:

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse()
           

在解析前,首先需要创建XMLMapperBuilder,创建过程如下:

private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
  // 将configuration赋给BaseBuilder
  super(configuration);
  // 创建MapperBuilderAssistant对象(该对象为MapperBuilder的协助者)
  this.builderAssistant = new  MapperBuilderAssistant(configuration, resource);
  this.parser = parser;
  this.sqlFragments = sqlFragments;
  this.resource = resource;
}
           

首先会初始化父类BaseBuilder,并将configuration赋给BaseBuilder; 然后创建MapperBuilderAssistant对象,该对象为XMLMapperBuilder的协助者,用来协助XMLMapperBuilder完成一些解析映射文件的动作。

当有了XMLMapperBuilder后,便可进入解析<mapper>的过程

public void parse() {
  // 若当前的Mapper.xml尚未被解析,则开始解析
  // PS:若<mappers>节点下有相同的<mapper>节点,那么就无需再次解析了
  if (!configuration.isResourceLoaded(resource)) {
    // 解析<mapper>节点
    configurationElement(parser.evalNode("/mapper"));
    // 将该Mapper.xml添加至configuration的LoadedResource容器中,下回无需再解析
    configuration.addLoadedResource(resource);
    // 将该Mapper.xml对应的Mapper Class注册进configuration的mapperRegistry容器中
    bindMapperForNamespace();
  }
 
  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}
           
private void configurationElement(XNode context) {
try {
  // 获取<mapper>节点上的namespace属性,该属性必须存在,表示当前映射文件对应的Mapper Class是谁
  String namespace = context.getStringAttribute("namespace");
  if (namespace == null || namespace.equals("")) {
    throw new BuilderException("Mapper's namespace cannot be empty");
  }
  // 将namespace属性值赋给builderAssistant
  builderAssistant.setCurrentNamespace(namespace);
  // 解析<cache-ref>节点
  cacheRefElement(context.evalNode("cache-ref"));
  // 解析<cache>节点
  cacheElement(context.evalNode("cache"));
  // 解析<parameterMap>节点
  parameterMapElement(context.evalNodes("/mapper/parameterMap"));
  // 解析<resultMap>节点
  resultMapElements(context.evalNodes("/mapper/resultMap"));
  // 解析<sql>节点
  sqlElement(context.evalNodes("/mapper/sql"));
  // 解析sql语句      
  buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
  throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
           

resultMapElements函数

该函数用于解析映射文件中所有的<resultMap>节点,这些节点会被解析成ResultMap对象,存储在Configuration对象的resultMaps容器中。

<resultMap>节点定义如下:

<resultMap id="BaseResultMap" type="com.softtool.mybatis.dao.Student">
    <constructor>
      <idArg column="id" javaType="java.lang.Integer" jdbcType="INTEGER" />
      <arg column="name" javaType="java.lang.String" jdbcType="VARCHAR" />
      <arg column="age" javaType="java.lang.Integer" jdbcType="INTEGER" />
      <arg column="sex" javaType="java.lang.String" jdbcType="VARCHAR" />
    </constructor>
  </resultMap>
           

<resultMap>节点的解析过程:

private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    String id = resultMapNode.getStringAttribute("id",
        resultMapNode.getValueBasedIdentifier());
    String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType"))));
    String extend = resultMapNode.getStringAttribute("extends");
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    Class<?> typeClass = resolveClass(type);
    Discriminator discriminator = null;
    List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
    resultMappings.addAll(additionalResultMappings);
    List<XNode> resultChildren = resultMapNode.getChildren();
    for (XNode resultChild : resultChildren) {
      if ("constructor".equals(resultChild.getName())) {
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
        ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }
           

ResultMapResolver这个类很纯粹,有且仅有一个函数resolve,用于构造ResultMap对象,并将其存入Configuration对象的resultMaps容器中;而这个过程是借助于MapperBuilderAssistant.addResultMap完成的。

sqlElement函数 该函数用于解析映射文件中所有的<sql>节点,并将这些节点存储在当前映射文件所对应的XMLMapperBuilder对象的sqlFragments容器中,供解析sql语句时使用。- buildStatementFromContext函数 该函数会将映射文件中的sql语句解析成MappedStatement对象,并存在configuration的mappedStatements。

转载于:https://my.oschina.net/u/4006362/blog/2252784