Mybatis源碼解析,一步一步從淺入深(五):mapper節點的解析

在上一篇文章Mybatis源碼解析,一步一步從淺入深(四):將configuration.xml的解析到Configuration對象實例中咱們談到了properties,settings,environments節點的解析,總結一下,針對示例工程的configuration.xml文件來講properties節點的解析就是將dbConfig.properties中的數據庫配置信息加載到了configuration實例的variables中,settings節點的解析讓configuration使用了咱們配置的log4j日誌系統,environments節點的解析生成了數據庫環境類(Environment)的實例對象,並將這個示例對象賦值給了 configuration的environment屬性。那麼接下來咱們着重看一下mappers節點的解析。mappers節點的解析很是重要,因此本文篇幅會很長。html

一,先看看示例工程的mappers節點和userDao-mapping.xml文件node

  mappers節點:sql

    

<!-- 映射文件,mybatis精髓 -->
  <mappers>
    <mapper resource="mapper/userDao-mapping.xml"/>
  </mappers>

  userDao-mapping.xml文件:數據庫

<?xml version="1.0" encoding="UTF-8" ?>   
<!DOCTYPE mapper   
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"  
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd"> 
<mapper namespace="com.zcz.learnmybatis.dao.UserDao">

   <select id="findUserById" resultType="com.zcz.learnmybatis.entity.User" > 
      select * from user where id = #{id}
   </select>

</mapper>

  userDao-mapping.xml文件很簡單,定義了一個namespace屬性指向UserDao接口,定義了一個select標籤聲明。apache

二,看一下解析mappers節點的方法mapperElement的源碼:網絡

  

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //檢測是不是package節點
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          //讀取<mapper resource="mapper/userDao-mapping.xml"/>中的mapper/userDao-mapping.xml,即resource = "mapper/userDao-mapping.xml"
          String resource = child.getStringAttribute("resource");
          //讀取mapper節點的url屬性
          String url = child.getStringAttribute("url");
          //讀取mapper節點的class屬性
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            //根據rusource加載mapper文件
            ErrorContext.instance().resource(resource);
            //讀取文件字節流
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //實例化mapper解析器
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            //執行解析mapper文件,即解析mapper/userDao-mapping.xml,文件
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            //從網絡url資源加載mapper文件
            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) {
            //使用mapperClass加載文件
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            //resource,url,mapperClass三種配置方法只能使用其中的一種,不然就報錯
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

  一目瞭然,遍歷mappers中的mapper節點,而後逐一解析。咱們的配置文件中只有一個mapper節點,因此這裏要解析的就是mapper/userDao-mapping.xml文件。mybatis

  解析mapper/userDao-mapping.xml文件的關鍵代碼是app

    //實例化mapper解析器
    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
    //執行解析mapper文件,即解析mapper/userDao-mapping.xml,文件
    mapperParser.parse();post

  接下來逐句進行分析fetch

三,實例化mapper解析器:XMLMapperBuilder 

  代碼:XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());

  查看mapper解析器XMLMapperBuilder類的聲明能夠發現,mapper解析器類XMLMapperBuilder和xml配置解析器XMLConfigBuilder同時繼承了父類BaseBuilder。其實後面還有幾個類也繼承了父類BaseBuilder。

  

public class XMLMapperBuilder extends BaseBuilder {}

  看一下使用到的XMLMapperBuilder構造方法

    

public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }

  這裏也建立了一個XPathParser xml文件解析器,緣由很簡單,由於mapper/userDao-mapping.xml也是一個xml文件(手動捂臉)。至於最後一個參數 Map<String, XNode> sqlFragments 是什麼?如今只知道sqlFragments = configuration.getSqlFragments(),可是具體是什麼呢?稍後纖細介紹。

  接下來使用this關鍵字,調用了XMLMapperBuilder的私有構造方法:

  

private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }

  看過Mybatis源碼解析,一步一步從淺入深(三):實例化xml配置解析器(XMLConfigBuilder)的同窗想必已經知道supper()方法作了什麼,這裏就再也不贅述了。值得一提的是MapperBuilderAssistant(mapper解析器助手),既然是助手,那確定就是輔助mapper解析器解析mapper文件的了。

  

public class MapperBuilderAssistant extends BaseBuilder {}

  看看MapperBuilderAssistant類的聲明,它也是繼承了BaseBuilder的。

  到這裏mapper解析器(XMLMapperBuilder)的實例化工做就已經完成了。可是爲了更好了進行接下來的分析,咱們有必要再認識Configuration類的一些屬性和方法:

public class Configuration {

  protected Environment environment;
  protected Properties variables = new Properties();
  ......
  //初始化值爲null
  protected String databaseId;
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  // 這是一個HashMap ,存放的是已經解析過的sql聲明,String  類型的鍵,例如com.zcz.learnmybatis.entity.User.findUserById,值是MappedStatement實例對象
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
  ......
  //這是一個無序不重複的Set集合,裏面存放的是已經加載解析過的 mapper文件名。例如<mapper resource="mapper/userDao-mapping.xml"/>中的mapper/userDao-mapping.xml
  protected final Set<String> loadedResources = new HashSet<String>();
  ......
  //sql碎片Map,鍵String 值XNode,這個Map中存放的是已經在先前的mapper中解析過的碎片
  protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
  ......
  public Configuration() {
    .....
    // 註冊默認的XML語言驅動
    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }
  ......
  //將resource 添加到 加載解析完成Set loadedResources中
  public void addLoadedResource(String resource) {
    loadedResources.add(resource);
  }
  //檢測mapper文件是否已經被加載解析過,resource是<mapper resource="mapper/userDao-mapping.xml"/>中的resource
  public boolean isResourceLoaded(String resource) {
    return loadedResources.contains(resource);
  }

  ......
 
  //根據標籤聲明類(MappedStatement) 實例對象的id獲取 解析過的標籤聲明類實例對象
// 標籤聲明是什麼,在下文中會給出解釋 public MappedStatement getMappedStatement(String id) { return this.getMappedStatement(id, true); } //根據標籤聲明類(MappedStatement) 實例對象的id獲取 解析過的標籤聲明類實例對象 public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } return mappedStatements.get(id); } //獲取sql碎片 public Map
<String, XNode> getSqlFragments() { return sqlFragments; } ...... // 根據檢查是否存在 標籤聲明名稱 爲statementName 的標籤聲明 public boolean hasStatement(String statementName) { return hasStatement(statementName, true); } // 根據檢查是否存在 標籤聲明名稱 爲statementName 的標籤聲明 public boolean hasStatement(String statementName, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } return mappedStatements.containsKey(statementName); } ...... }

四,執行解析mapper文件,即解析mapper/userDao-mapping.xml文件

  代碼:mapperParser.parse();

  看一下負責解析mapper文件的parser方法的源代碼:

  public void parse() {
    // 先判斷mapper文件是否已經解析
    if (!configuration.isResourceLoaded(resource)) {
      //執行解析
      configurationElement(parser.evalNode("/mapper"));
      //保存解析記錄
      configuration.addLoadedResource(resource);
      // 綁定已經解析的命名空間
      bindMapperForNamespace();
    }

    ......
  }

  很明顯,真正解析mapper文件的代碼是configurationElement方法:

  

private void configurationElement(XNode context) {
    try {
      //獲取mapper文件中的mapper節點的namespace屬性 com.zcz.learnmybatis.entity.UserDao
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
          throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //將namespace賦值給映射 mapper解析器助理builderAssistant.currentNameSpace,即告訴mapper解析器助理如今解析的是那個mapper文件
      builderAssistant.setCurrentNamespace(namespace);
      //解析cache-ref節點
      cacheRefElement(context.evalNode("cache-ref"));
      //解析cache節點
      cacheElement(context.evalNode("cache"));
      //解析parameterMap節點,這裏爲何要使用"/mapper/parameterMap"而不是直接使用"parameterMap",由於parameterMap能夠配置多個,並且使用的是context.evalNodes方法,注意不是evalNode了,是evalNodes。
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析resultMap節點
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql節點
      sqlElement(context.evalNodes("/mapper/sql"));
      //解析select|insert|update|delete節點,注意context.evalNodes()方法,返回的是一個List集合。
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

  從上面代碼看到,處理完namespace以後,就是解析mapper文件中的節點了,可是咱們的userDao-mapping.xml文件中只有一個select標籤:

    

<select id="findUserById" resultType="com.zcz.learnmybatis.entity.User" > 
      select * from user where id = #{id}
   </select>

  那麼只須要分析最有一個方法buildStatementFromContext就能夠了。

  看源碼:

  這個方法中的惟一的參數list 就是userDao-mapping.xml中的select,update,delete,insert標籤們。注意是一個List。也就是說可能會有多個。

 private void buildStatementFromContext(List<XNode> list) {
   //這裏的confuration.getDatabaseId 是 null ,由於 configuration初始化時沒有給默認值,在虛擬機實例化configuration對象時,賦予默認值null
if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); }

  又調用了buildStatementFromContext 重載方法:

  在這個方法中遍歷了上面咱們提到的list.而咱們的userDao-mapping.xml中的select標籤就是在這個list中。

  咱們都知道,在mapper文件中,select標籤,update標籤,delete標籤,insert標籤的id屬性對應着namespace中的接口的方法名。因此咱們就稱一個select標籤,update標籤,delete標籤或者一個insert標籤爲一個標籤聲明。

  那麼這個方法中就是遍歷了全部的標籤聲明,並逐一解析。

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
    //初始化標籤聲明解析器(XMLStatementBuilder)
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try {
      // 執行標籤聲明的解析 statementParser.parseStatementNode(); }
catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }

  到如今咱們終於明白,原來buildStatementFromContext 也是不負責解析的,真正負責解析的是final 修飾的 XMLStatementBuilder類 的實例對象 statementParser。

  

public class XMLStatementBuilder extends BaseBuilder {}

  發現了什麼?

  XMLStatementBuilder也是繼承BaseBuilder的。

  看看構造方法:

  

  public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
    super(configuration);
    this.builderAssistant = builderAssistant;
    this.context = context;
    this.requiredDatabaseId = databaseId;
  }

  只有一些賦值操做。

  重點就是try-catch塊中的執行標籤聲明的解析的:statementParser.parseStatementNode();這一句代碼了。

  看源碼:

    

 1 public void parseStatementNode() {
 2     // 獲取的是select 標籤的id屬性,即id="findUserById"
 3     String id = context.getStringAttribute("id");
 4     // 沒有databaseId屬性,即databaseId = null;
 5     String databaseId = context.getStringAttribute("databaseId");
 6    // 判斷databaseId,這一行代碼下方有詳細介紹 
 7     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
 8     // 沒有fetchSize屬性,即fetchSize = null;
 9     Integer fetchSize = context.getIntAttribute("fetchSize");
10     // 沒有timeout屬性,即timeout = null;
11     Integer timeout = context.getIntAttribute("timeout");
12     // 沒有 parameterMap 屬性,即 parameterMap = null;
13     String parameterMap = context.getStringAttribute("parameterMap");
14     // 沒有 parameterType 屬性,即 parameterType = null;
15     String parameterType = context.getStringAttribute("parameterType");
16     // parameterType = null,即parameterTypeClass = null
17     Class<?> parameterTypeClass = resolveClass(parameterType);
18     // 沒有 resultMap 屬性,即 resultMap = null;
19     String resultMap = context.getStringAttribute("resultMap");
20     // 獲取的是select 標籤的resultType屬性,即resultType="com.zcz.learnmybatis.entity.User"
21     String resultType = context.getStringAttribute("resultType");
22     // 沒有 lang 屬性,即 lang = null;
23     String lang = context.getStringAttribute("lang");
24     //獲取默認的語言驅動 XMLLanguageDriver
25     LanguageDriver langDriver = getLanguageDriver(lang);
26 
27     // 獲取User類的類對象
28     Class<?> resultTypeClass = resolveClass(resultType);
29     // 沒有 resultSetType 屬性,即 resultSetType = null;
30     String resultSetType = context.getStringAttribute("resultSetType");
31     // 沒有 statementType 屬性,返回默認的 「PREPARED」 即statementType = PREPARED
32     StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
33     // 沒有 resultSetType 屬性,即 resultSetType = null;
34     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
35     
36     // nodeName = "select"
37     String nodeName = context.getNode().getNodeName();
38     // sql類型是 sqlCommandType = SELECT
39     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
40     // isSelect = true;
41     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
42     // 沒有 flushCache 屬性,即 flushCache = null; 取默認flushCache = !isSelect = false;
43     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
44     // 沒有 useCache 屬性,即 useCache = null; 取默認useCache = isSelect= true;
45     boolean useCache = context.getBooleanAttribute("useCache", isSelect);
46     // 沒有 resultOrdered 屬性,即 resultOrdered = null; 取默認resultOrdered= false;
47     boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
48 
49     // Include Fragments before parsing
50     // 處理include 標籤,咱們的select標籤中沒有用到include標籤
51     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
52     includeParser.applyIncludes(context.getNode());
53 
54     // Parse selectKey after includes and remove them.
55     // 處理selectKey 標籤,咱們的select標籤中沒有用到selectKey標籤
56     processSelectKeyNodes(id, parameterTypeClass, langDriver);
57 
58     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
59     // 在解析完<selectKey> 和 <include> 以後開始解析 SQL語句
60     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
61     // 沒有 resultSets 屬性,即 resultSets = null;
62     String resultSets = context.getStringAttribute("resultSets");
63     // 沒有 keyProperty 屬性,即 keyProperty = null;
64     String keyProperty = context.getStringAttribute("keyProperty");
65     // 沒有 keyColumn 屬性,即 keyColumn = null;
66     String keyColumn = context.getStringAttribute("keyColumn");
67     
68     //接下來處理的是主鍵生成器
69     KeyGenerator keyGenerator;
70     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
71     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
72     if (configuration.hasKeyGenerator(keyStatementId)) {
73       keyGenerator = configuration.getKeyGenerator(keyStatementId);
74     } else {
75       // 應爲咱們的是select類型的語句,因此SqlCommandType.INSERT.equals(sqlCommandType) == false。因此keyGenerator = new NoKeyGenerator()
76       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
77           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
78           ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
79     }
80     
81     // 這一步 就是讓mapper解析器助理建立MappedStatement實例對象,並將新建的實例對象添加到 configuration的 mappedStatements中,表示這個標籤聲明被解析過了。
82     //根據上方的解析過程,咱們能夠清晰的知道各個參數的值:
83     //id = "findUserById",sqlSource = RawSqlSource 實例對象,statementType = PREPARED,sqlCommandType = SELECT
84     //fetchSize,timeout,parameterMap,parameterTypeClass,resultMap= null
85     //resultTypeClass = User類對象
86     //resultSetTypeEnum = null
87     //flushCache=false,useCache=true,resultOrdered=false,keyGenerator = new NoKeyGenerator()
88     //keyProperty, keyColumn, databaseId,=null
89     //langDriver=XMLLanguageDriver實例對象
90     //resultSets=null。
91     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
92         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
93         resultSetTypeEnum, flushCache, useCache, resultOrdered, 
94         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
95 }

 

  經過代碼中的註釋,相信你們都能看的明白,這裏着重解釋一下,第7行,第60行,第91行:

  第7行:if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;中的databaseIdMatchesCurrent方法源碼:  

 1 //比較須要使用的databaseId 和 標籤聲明中的databaseId 是否相同,這時id="findUserById",同時databaseId和requiredDatabaseId 都是null
 2   private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
 3     if (requiredDatabaseId != null) {
 4       if (!requiredDatabaseId.equals(databaseId)) {
 5         //若是不一樣就返回false,中止解析
 6         return false;
 7       }
 8     } else {
 9       if (databaseId != null) {
10         // 這個時候requiredDatabaseId == null, 在這個requiredDatabaseId 等於 null的狀況下databaseId 卻不等於null,說明須要使用的databaseId 和 標籤聲明中的databaseId 是不相同的,就放回false,中止解析
11         return false;
12       }
13       // skip this statement if there is a previous one with a not null databaseId 若是存在已經解析過的而且databaseId不爲null的標籤聲明,則返回false跳過解析
14       // 獲取id,這個id就是標籤解析器的id,從接下來的分析中能夠明確看出:這個id也是標籤聲明類(MappedStatement)實例化對象的id。
15       id = builderAssistant.applyCurrentNamespace(id, false);
16       
17       if (this.configuration.hasStatement(id, false)) {
18         MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
19         if (previous.getDatabaseId() != null) {
20           return false;
21         }
22       }
23     }
24     return true;
25   }

  而源碼中的applyCurrentNamespace源碼是:

    

public String applyCurrentNamespace(String base, boolean isReference) {
    if (base == null) return null;
    if (isReference) {
      // is it qualified with any namespace yet?
      if (base.contains(".")) return base;
    } else {
      // is it qualified with this namespace yet?
      if (base.startsWith(currentNamespace + ".")) return base;
      if (base.contains(".")) throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
    }
    // 返回com.zcz.learnmybatis.entity.UserDao.findUserById
    // currentNamespace 在前面已經設置過了,就是mapper文件中的namespace
    return currentNamespace + "." + base;
  }

  第60行是用來處理SQL語句的,也就是用來處理:

    

 select * from user where id = #{id}

  這一部分的,處理了什麼呢?簡單來講就是根據」${「是否存在來判斷SQL語句是不是動態SQL語句,而且把 select * from user where id = #{id} 轉換爲select * from user where id = ?。同時把#{id}中的id保存起來。具體細節源碼不展開了,須要的話,再寫一篇文章詳細解析吧。

  第91行,就是把一個select ,update,delete 或者insert標籤聲明轉換爲MappedStatement對象實例,更明白點說,就是把

  

<select id="findUserById" resultType="com.zcz.learnmybatis.entity.User" > 
      select * from user where id = #{id}
   </select>

  這一部分轉換爲MappedStatement對象並保持到configuration中,實現保存的代碼源碼以下:

  可是要注意下面代碼裏的id = "findUserById",可是通過applyCurrentNameSpace()方法後,id= "com.zcz.learnmybatis.dao.UserDao.findUserById",即在原來的id前,添加了UserDao類全包名+類名+「.」;

public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {
    
    if (unresolvedCacheRef) throw new IncompleteElementException("Cache-ref not yet resolved");
    
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  //初始化MappenStatement.Builder
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
    statementBuilder.resource(resource);
    statementBuilder.fetchSize(fetchSize);
    statementBuilder.statementType(statementType);
    statementBuilder.keyGenerator(keyGenerator);
    statementBuilder.keyProperty(keyProperty);
    statementBuilder.keyColumn(keyColumn);
    statementBuilder.databaseId(databaseId);
    statementBuilder.lang(lang);
    statementBuilder.resultOrdered(resultOrdered);
    statementBuilder.resulSets(resultSets);
    setStatementTimeout(timeout, statementBuilder);

    setStatementParameterMap(parameterMap, parameterType, statementBuilder);
    setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
    setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
  // 構造MappedStatement
    MappedStatement statement = statementBuilder.build();
  // 保存 configuration.addMappedStatement(statement);
return statement; }

  值得一提的是在MappedStatement statement = statementBuilder.build();咱們先看看源碼:

  

1 public MappedStatement build() {
2       assert mappedStatement.configuration != null;
3       assert mappedStatement.id != null;
4       assert mappedStatement.sqlSource != null;
5       assert mappedStatement.lang != null;
6       mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
7       return mappedStatement;
8     }

  在第六行有一個Collections.unmodifiableList方法,這個方法是一個頗有趣的方法,想要了解一下的話,請查閱:Collections.unmodifiableMap,Collections.unmodifiableList,Collections.unmodifiableSet做用及源碼解析

  

到這裏 select的標籤聲明的解析就結束了,同時Mapper文件的解析也結束了。

總結一下,mappers節點解析完成以後,全部的mybatis有關的配置文件都已經解析完成了,包括:configuration.xml文件,dbConfig.properties文件,userDa0-mapping.xml文件。而且都保存到Configuration 的實例化對象中了。

    


 原創不易,轉載請聲明出處:http://www.javashuo.com/article/p-yxzevcoj-n.html 

相關文章
相關標籤/搜索