MyBatis源碼解析(一)——MyBatis初始化過(guò)程解析

1. 準(zhǔn)備工作

為了看清楚MyBatis的整個(gè)初始化過(guò)程,先創(chuàng)建一個(gè)簡(jiǎn)單的Java項(xiàng)目,目錄結(jié)構(gòu)如下圖所示:


1.1 Product 產(chǎn)品實(shí)體類

public class Product {
    private long id;
    private String productName;
    private String productContent;
    private String price;
    private int sort;
    private int falseSales;
    private long category_id;
    private byte type;
    private byte state;
    // PS:省略setter、getter函數(shù)
}

1.2 ProductMapper 產(chǎn)品持久化接口

public interface ProductMapper {
    /**
     * 查詢所有的產(chǎn)品
     * @return
     */
    List<Product> selectProductList();
}

1.3 ProductMapper.xml 產(chǎn)品映射文件

<?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="team.njupt.mapper.ProductMapper">
    <select id="selectProductList" resultType="team.njupt.entity.Product">
        select * from product
    </select>
</mapper>

1.4 db.properties 數(shù)據(jù)庫(kù)配置文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/waimai?useUnicode=true&characterEncoding=utf8
username=root
password=xxxxxx

1.5 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>
    <properties resource="db.properties">
        <!--<property name="username" value="dev_user"/>-->
        <!--<property name="password" value="F2Fa3!33TYyg"/>-->
    </properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <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="team/njupt/mapper/ProductMapper.xml"/>
    </mappers>
</configuration>

1.6 Main 主函數(shù)

public class Main {
    public static void main(String[] args) throws IOException {

        String resource = "mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
            List<Product> productList = productMapper.selectProductList();
            for (Product product : productList) {
                System.out.printf(product.toString());
            }
        } finally {
            sqlSession.close();
        }
    }
}

2. MyBatis初始化過(guò)程

2.1 獲取配置文件

當(dāng)系統(tǒng)初始化時(shí),首先會(huì)讀取配置文件,并將其解析成InputStream

String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

2.2 創(chuàng)建SqlSessionFactoryBuilder對(duì)象

SqlSessionFactoryBuilder的名字中可以看出,SqlSessionFactoryBuilder是用來(lái)創(chuàng)建SqlSessionFactory對(duì)象的。
來(lái)看一下SqlSessionFactoryBuilder源碼:


SqlSessionFactoryBuilder中只有一些重載的build函數(shù),這些build函數(shù)的入?yún)⒍际荕yBatis配置文件的輸入流,返回值都是SqlSessionFactory;由此可見,SqlSessionFactoryBuilder的作用很純粹,就是用來(lái)通過(guò)配置文件創(chuàng)建SqlSessionFactory對(duì)象的。

2.3 SqlSessionFactory創(chuàng)建過(guò)程

下面具體來(lái)看一下,build函數(shù)是如何創(chuàng)建SqlSessionFactory對(duì)象的。

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

2.3.1 構(gòu)造XMLConfigBuilder對(duì)象

build函數(shù)首先會(huì)構(gòu)造一個(gè)XMLConfigBuilder對(duì)象,從名字上大致可以猜到,該對(duì)象是用來(lái)解析XML配置文件的。下面來(lái)看一下XMLConfigBuilder的體系結(jié)構(gòu)。

  • XMLxxxBuilder是用來(lái)解析XML配置文件的,不同類型XMLxxxBuilder用來(lái)解析MyBatis配置文件的不同部位。比如:XMLConfigBuilder用來(lái)解析MyBatis的配置文件,XMLMapperBuilder用來(lái)解析MyBatis中的映射文件(如上文提到的ProductMapper.xml),XMLStatementBuilder用來(lái)解析映射文件中的SQL語(yǔ)句。

  • 這些XMLxxxBuilder都有一個(gè)共同的父類——BaseBuilder。這個(gè)父類維護(hù)了一個(gè)全局的Configuration對(duì)象,MyBatis的配置文件解析后就以Configuration對(duì)象的形式存儲(chǔ)。

  • 當(dāng)創(chuàng)建XMLConfigBuilder對(duì)象時(shí),就會(huì)初始化Configuration對(duì)象,并且在初始化Configuration對(duì)象的時(shí)候,一些別名會(huì)被注冊(cè)到ConfigurationtypeAliasRegistry容器中。

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

2.3.2 解析配置文件

當(dāng)有了XMLConfigBuilder對(duì)象之后,接下來(lái)就可以用它來(lái)解析配置文件了。

  private void parseConfiguration(XNode root) {
  try {
    // 解析<properties>節(jié)點(diǎn)
    propertiesElement(root.evalNode("properties"));
    // 解析<settings>節(jié)點(diǎn)
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    // 解析<typeAliases>節(jié)點(diǎn)
    typeAliasesElement(root.evalNode("typeAliases"));
    // 解析<plugins>節(jié)點(diǎn)
    pluginElement(root.evalNode("plugins"));
    // 解析<objectFactory>節(jié)點(diǎn)
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    // 解析<reflectorFactory>節(jié)點(diǎn)
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // 解析<environments>節(jié)點(diǎn)
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 解析<mappers>節(jié)點(diǎn)
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

從上述代碼中可以看到,XMLConfigBuilder會(huì)依次解析配置文件中的<properties>、< settings >、< environments>、< typeAliases >< plugins >、< mappers >等屬性。下面介紹下幾個(gè)重要屬性的解析過(guò)程。

2.3.2.1 <properties>節(jié)點(diǎn)的解析過(guò)程

  • <properties>節(jié)點(diǎn)的定義如下:

    <properties resource="org/mybatis/example/config.properties">
      <property name="username" value="dev_user"/>
      <property name="password" value="F2Fa3!33TYyg"/>
    </properties>
    
  • <properties>節(jié)點(diǎn)的解析過(guò)程:

    /**
      * @Param context <properties>節(jié)點(diǎn)
      */
    private void propertiesElement(XNode context) throws Exception {
      if (context != null) {
        // 獲取<properties>節(jié)點(diǎn)的所有子節(jié)點(diǎn)
        Properties defaults = context.getChildrenAsProperties();
        // 獲取<properties>節(jié)點(diǎn)上的resource屬性
        String resource = context.getStringAttribute("resource");
        // 獲取<properties>節(jié)點(diǎn)上的url屬性
        String url = context.getStringAttribute("url");
        // resource和url不能同時(shí)存在
        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) {
          // 獲取resource屬性值對(duì)應(yīng)的properties文件中的鍵值對(duì),并添加至defaults容器中        
          defaults.putAll(Resources.getResourceAsProperties(resource));
        } else if (url != null) {
          // 獲取url屬性值對(duì)應(yīng)的properties文件中的鍵值對(duì),并添加至defaults容器中
          defaults.putAll(Resources.getUrlAsProperties(url));
        }
        // 獲取configuration中原本的屬性,并添加至defaults容器中
        Properties vars = configuration.getVariables();
        if (vars != null) {
          defaults.putAll(vars);
        }
        parser.setVariables(defaults);
        // 將defaults容器添加至configuration中
        configuration.setVariables(defaults);
      }
    }
    
    • 首先讀取<resources>節(jié)點(diǎn)下的所有<resource>節(jié)點(diǎn),并將每個(gè)節(jié)點(diǎn)的namevalue屬性存入Properties中。
    • 然后讀取<resources>節(jié)點(diǎn)上的resource、url屬性,并獲取指定配置文件中的namevalue,也存入Properties中。(PS:由此可知,如果resource節(jié)點(diǎn)上定義的屬性和properties文件中的屬性重名,那么properties文件中的屬性值會(huì)覆蓋resource節(jié)點(diǎn)上定義的屬性值。)
    • 最終,攜帶所有屬性的Properties對(duì)象會(huì)被存儲(chǔ)在Configuration對(duì)象中。

2.3.2.2 <settings>節(jié)點(diǎn)的解析過(guò)程

  • <settings>節(jié)點(diǎn)的定義如下:
    <settings>
      <setting name="cacheEnabled" value="true"/>
      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="multipleResultSetsEnabled" value="true"/>
    </settings>
    
  • <settings>節(jié)點(diǎn)的解析過(guò)程:
    <settings>屬性的解析過(guò)程和 <properties>屬性的解析過(guò)程極為類似,這里不再贅述。最終,所有的setting屬性都被存儲(chǔ)在Configuration對(duì)象中。

2.3.2.3 <typeAliases>屬性的解析過(guò)程

<typeAliases>屬性的定義方式有如下兩種:

  • 方式1:
    <typeAliases>
      <typeAlias alias="Author" type="domain.blog.Author"/>
      <typeAlias alias="Blog" type="domain.blog.Blog"/>
    </typeAliases>
    
  • 方式2:
    <typeAliases>
      <package name="domain.blog"/>
    </typeAliases>
    
    采用這種方式時(shí),MyBatis會(huì)為指定包下的所有類起一個(gè)別名,該別名為首字母小寫的類名。

<typeAliases>節(jié)點(diǎn)的解析過(guò)程如下:

  private void typeAliasesElement(XNode parent) {
  if (parent != null) {
    // 遍歷<typeAliases>下的所有子節(jié)點(diǎn)
    for (XNode child : parent.getChildren()) {
      // 若當(dāng)前結(jié)點(diǎn)為<package>
      if ("package".equals(child.getName())) {
        // 獲取<package>上的name屬性(包名)
        String typeAliasPackage = child.getStringAttribute("name");
        // 為該包下的所有類起個(gè)別名,并注冊(cè)進(jìn)configuration的typeAliasRegistry中          
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      } 
      // 如果當(dāng)前結(jié)點(diǎn)為< typeAlias >
      else {
        // 獲取alias和type屬性
        String alias = child.getStringAttribute("alias");
        String type = child.getStringAttribute("type");
        // 注冊(cè)進(jìn)configuration的typeAliasRegistry中
        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);
        }
      }
    }
  }
}
  • 如果<typeAliases>節(jié)點(diǎn)下定義了<package>節(jié)點(diǎn),那么MyBatis會(huì)給該包下的所有類起一個(gè)別名(以類名首字母小寫作為別名)
  • 如果<typeAliases>節(jié)點(diǎn)下定義了<typeAlias>節(jié)點(diǎn),那么MyBatis就會(huì)給指定的類起指定的別名。
  • 這些別名都會(huì)被存入configurationtypeAliasRegistry容器中。

2.3.2.4 <mappers>節(jié)點(diǎn)的解析過(guò)程

<mappers>節(jié)點(diǎn)的定義方式有如下四種:

  • 方式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>節(jié)點(diǎn)的解析過(guò)程如下:

  private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    // 遍歷<mappers>下所有子節(jié)點(diǎn)
    for (XNode child : parent.getChildren()) {
      // 如果當(dāng)前節(jié)點(diǎn)為<package>
      if ("package".equals(child.getName())) {
        // 獲取<package>的name屬性(該屬性值為mapper class所在的包名)
        String mapperPackage = child.getStringAttribute("name");
        // 將該包下的所有Mapper Class注冊(cè)到configuration的mapperRegistry容器中
        configuration.addMappers(mapperPackage);
      } 
      // 如果當(dāng)前節(jié)點(diǎn)為<mapper>
      else {
        // 依次獲取resource、url、class屬性
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        // 解析resource屬性(Mapper.xml文件的路徑)
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          // 將Mapper.xml文件解析成輸入流
          InputStream inputStream = Resources.getResourceAsStream(resource);
          // 使用XMLMapperBuilder解析Mapper.xml,并將Mapper Class注冊(cè)進(jìn)configuration對(duì)象的mapperRegistry容器中
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          mapperParser.parse();
        } 
        // 解析url屬性(Mapper.xml文件的路徑)
        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();
        } 
        // 解析class屬性(Mapper Class的全限定名)
        else if (resource == null && url == null && mapperClass != null) {
          // 將Mapper Class的權(quán)限定名轉(zhuǎn)化成Class對(duì)象
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          // 注冊(cè)進(jìn)configuration對(duì)象的mapperRegistry容器中
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}
  • MyBatis會(huì)遍歷<mappers>下所有的子節(jié)點(diǎn),如果當(dāng)前遍歷到的節(jié)點(diǎn)是<package>,則MyBatis會(huì)將該包下的所有Mapper Class注冊(cè)到configurationmapperRegistry容器中。
  • 如果當(dāng)前節(jié)點(diǎn)為<mapper>,則會(huì)依次獲取resource、url、class屬性,解析映射文件,并將映射文件對(duì)應(yīng)的Mapper Class注冊(cè)到configurationmapperRegistry容器中。

其中,<mapper>節(jié)點(diǎn)的解析過(guò)程如下:

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
  • 在解析前,首先需要?jiǎng)?chuàng)建XMLMapperBuilder,創(chuàng)建過(guò)程如下:

    private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
      // 將configuration賦給BaseBuilder
      super(configuration);
      // 創(chuàng)建MapperBuilderAssistant對(duì)象(該對(duì)象為MapperBuilder的協(xié)助者)
      this.builderAssistant = new  MapperBuilderAssistant(configuration, resource);
      this.parser = parser;
      this.sqlFragments = sqlFragments;
      this.resource = resource;
    }
    
    • 首先會(huì)初始化父類BaseBuilder,并將configuration賦給BaseBuilder;
    • 然后創(chuàng)建MapperBuilderAssistant對(duì)象,該對(duì)象為XMLMapperBuilder的協(xié)助者,用來(lái)協(xié)助XMLMapperBuilder完成一些解析映射文件的動(dòng)作。
  • 當(dāng)有了XMLMapperBuilder后,便可進(jìn)入解析<mapper>的過(guò)程:

    public void parse() {
      // 若當(dāng)前的Mapper.xml尚未被解析,則開始解析
      // PS:若<mappers>節(jié)點(diǎn)下有相同的<mapper>節(jié)點(diǎn),那么就無(wú)需再次解析了
      if (!configuration.isResourceLoaded(resource)) {
        // 解析<mapper>節(jié)點(diǎn)
        configurationElement(parser.evalNode("/mapper"));
        // 將該Mapper.xml添加至configuration的LoadedResource容器中,下回?zé)o需再解析
        configuration.addLoadedResource(resource);
        // 將該Mapper.xml對(duì)應(yīng)的Mapper Class注冊(cè)進(jìn)configuration的mapperRegistry容器中
        bindMapperForNamespace();
      }
    
      parsePendingResultMaps();
      parsePendingCacheRefs();
      parsePendingStatements();
    }
    
  • configurationElement函數(shù)

    private void configurationElement(XNode context) {
    try {
      // 獲取<mapper>節(jié)點(diǎn)上的namespace屬性,該屬性必須存在,表示當(dāng)前映射文件對(duì)應(yīng)的Mapper Class是誰(shuí)
      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>節(jié)點(diǎn)
      cacheRefElement(context.evalNode("cache-ref"));
      // 解析<cache>節(jié)點(diǎn)
      cacheElement(context.evalNode("cache"));
      // 解析<parameterMap>節(jié)點(diǎn)
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      // 解析<resultMap>節(jié)點(diǎn)
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      // 解析<sql>節(jié)點(diǎn)
      sqlElement(context.evalNodes("/mapper/sql"));
      // 解析sql語(yǔ)句      
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
    }
    
  • resultMapElements函數(shù)
    該函數(shù)用于解析映射文件中所有的<resultMap>節(jié)點(diǎn),這些節(jié)點(diǎn)會(huì)被解析成ResultMap對(duì)象,存儲(chǔ)在Configuration對(duì)象的resultMaps容器中。

    • <resultMap>節(jié)點(diǎn)定義如下:
     <resultMap id="userResultMap" type="User">
      <constructor>
         <idArg column="id" javaType="int"/>
         <arg column="username" javaType="String"/>
      </constructor>
      <result property="username" column="user_name"/>
      <result property="password" column="hashed_password"/>
    </resultMap>
    
    • <resultMap>節(jié)點(diǎn)的解析過(guò)程:
    private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
      ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
      // 獲取<ResultMap>上的id屬性
      String id = resultMapNode.getStringAttribute("id",
        resultMapNode.getValueBasedIdentifier());
      // 獲取<ResultMap>上的type屬性(即resultMap的返回值類型)
      String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType"))));
      // 獲取extends屬性
      String extend = resultMapNode.getStringAttribute("extends");
      // 獲取autoMapping屬性
      Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
      // 將resultMap的返回值類型轉(zhuǎn)換成Class對(duì)象
      Class<?> typeClass = resolveClass(type);
      Discriminator discriminator = null;
      // resultMappings用于存儲(chǔ)<resultMap>下所有的子節(jié)點(diǎn)
      List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
      resultMappings.addAll(additionalResultMappings);
      // 獲取并遍歷<resultMap>下所有的子節(jié)點(diǎn)
      List<XNode> resultChildren = resultMapNode.getChildren();
      for (XNode resultChild : resultChildren) {
        // 若當(dāng)前節(jié)點(diǎn)為<constructor>,則將它的子節(jié)點(diǎn)們添加到resultMappings中去
        if ("constructor".equals(resultChild.getName())) {
          processConstructorElement(resultChild, typeClass, resultMappings);
        }
        // 若當(dāng)前節(jié)點(diǎn)為<discriminator>,則進(jìn)行條件判斷,并將命中的子節(jié)點(diǎn)添加到resultMappings中去
        else if ("discriminator".equals(resultChild.getName())) {
          discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
        }
        // 若當(dāng)前節(jié)點(diǎn)為<result>、<association>、<collection>,則將其添加到resultMappings中去
        else {
          // PS:flags僅用于區(qū)分當(dāng)前節(jié)點(diǎn)是否是<id>或<idArg>,因?yàn)檫@兩個(gè)節(jié)點(diǎn)的屬性名為name,而其他節(jié)點(diǎn)的屬性名為property
          List<ResultFlag> flags = new ArrayList<ResultFlag>();
          if ("id".equals(resultChild.getName())) {
            flags.add(ResultFlag.ID);
          }
          resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
        }
      }
      // ResultMapResolver的作用是生成ResultMap對(duì)象,并將其加入到Configuration對(duì)象的resultMaps容器中(具體過(guò)程見下)
      ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
      try {
        return resultMapResolver.resolve();
      } catch (IncompleteElementException  e) {
        configuration.addIncompleteResultMap(resultMapResolver);
        throw e;
      }
    }
    

    ResultMapResolver這個(gè)類很純粹,有且僅有一個(gè)函數(shù)resolve,用于構(gòu)造ResultMap對(duì)象,并將其存入Configuration對(duì)象的resultMaps容器中;而這個(gè)過(guò)程是借助于MapperBuilderAssistant.addResultMap完成的。

    public ResultMap resolve() {
      return assistant.addResultMap(this.id, this.type, this.extend,  this.discriminator, this.resultMappings, this.autoMapping);
    }
    
  • sqlElement函數(shù)
    該函數(shù)用于解析映射文件中所有的<sql>節(jié)點(diǎn),并將這些節(jié)點(diǎn)存儲(chǔ)在當(dāng)前映射文件所對(duì)應(yīng)的XMLMapperBuilder對(duì)象的sqlFragments容器中,供解析sql語(yǔ)句時(shí)使用。

    <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
    
  • buildStatementFromContext函數(shù)
    該函數(shù)會(huì)將映射文件中的sql語(yǔ)句解析成MappedStatement對(duì)象,并存在configurationmappedStatements

2.3.3 創(chuàng)建SqlSessionFactory對(duì)象

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

回過(guò)頭來(lái)再看一下SqlSessionFactorybuild函數(shù),剛才說(shuō)了半天,介紹了XMLConfigBuilder解析映射文件的過(guò)程,解析完成之后parser.parse()函數(shù)會(huì)返回一個(gè)包含了映射文件解析結(jié)果的configuration對(duì)象,緊接著,這個(gè)對(duì)象將作為參數(shù)傳遞給另一個(gè)build函數(shù),如下:

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

這個(gè)函數(shù)將configuration作為參數(shù),創(chuàng)建了DefaultSqlSessionFactory對(duì)象。
DefaultSqlSessionFactory是接口SqlSessionFactory的一個(gè)實(shí)現(xiàn)類,SqlSessionFactory的體系結(jié)構(gòu)如下圖所示:

此時(shí),SqlSessionFactory創(chuàng)建完畢!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 引言# 本文主要講解JDBC怎么演變到Mybatis的漸變過(guò)程,重點(diǎn)講解了為什么要將JDBC封裝成Mybait...
    七寸知架構(gòu)閱讀 77,609評(píng)論 36 979
  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥慢飛閱讀 6,284評(píng)論 0 4
  • 1 緩存介紹# MyBatis支持聲明式數(shù)據(jù)緩存(declarative data caching)。當(dāng)一條SQL...
    七寸知架構(gòu)閱讀 2,230評(píng)論 2 51
  • 記錄是一種精神,是加深理解最好的方式之一。 最近看了下Mybatis的源碼,了解了下Mybatis對(duì)配置文件的解析...
    曹金桂閱讀 7,755評(píng)論 3 35
  • AsstPro是華自科技開發(fā)的一款用于歸檔、下載、發(fā)布的工具。目前,它提供“工程項(xiàng)目”和“公司產(chǎn)品”兩大部分...
    AsstPro閱讀 1,113評(píng)論 0 1

友情鏈接更多精彩內(nèi)容