天天看點

Mybatis源碼 ------ xml配置檔案解析

  在不整合spring的情況下,通過mybatis的xml配置問你件mybatis的各種屬性,完成SqlSessionFactory初始化。

public SqlSessionFactory getSqlSessionFactory() throws IOException {
        return new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    }
           
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //通過XMLConfigBuilder對象 解析 xml配置檔案
      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.
      }
    }
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
	//父類的configuration屬性指派為new Configuration() , configuration對象很重要 , 裡面儲存非常多的屬性。
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    //configuration的的variables設定為props
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    //parser指派為parser
    this.parser = parser;
}
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //從根标簽configuration開始解析
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}
           
private void parseConfiguration(XNode root) {
    try {
      //解析properties标簽  比如<properties resource="database.properties"/> 引入配置資訊 
      //使得可以在xml配置檔案中使用${}占位符擷取相應的值
      propertiesElement(root.evalNode("properties"));
      //解析settings标簽 擷取子标簽setting的name value屬性,封裝為Properties對象傳回
      //<setting name="safeResultHandlerEnabled" value="true"/> 開啟自動駝峰轉換
      //<setting name="cacheEnabled" value="true"/> 開啟二級緩存功能 (一般不去使用,特别是分布式的環境)
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      //擷取vfsImpl屬性值,設定到configuration的vfsImpl屬性中
      //且再VFS類的USER_IMPLEMENTATIONS容器中放入該vfsImpl對應的class對象
      //一般使用預設的DefaultVFS
      loadCustomVfs(settings);
      //擷取到logImpl的屬性值,設定到configuration的logImpl屬性中
      //且把執行個體化該屬性值對應的類的構造函數,把該日志構造函數設定到LogFactory的logConstructor屬性中
      loadCustomLogImpl(settings);
      //解析typeAliases标簽
      typeAliasesElement(root.evalNode("typeAliases"));
      //解析plugins标簽
      pluginElement(root.evalNode("plugins"));
      //這幾個屬性一般使用預設的就行,資料庫字段映射到實體類用到的類
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      //将setting标簽配置的各種屬性封裝到configuration對應的屬性中
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      //解析environments标簽
      environmentsElement(root.evalNode("environments"));
      //解析databaseIdProvider标簽
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //解析typeHandlers标簽
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析mappers标簽
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
           

  解析properties标簽

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      //擷取子标簽的name value屬性,封裝為Properties對象
      Properties defaults = context.getChildrenAsProperties();
      //擷取resource屬性的值
      String resource = context.getStringAttribute("resource");
      //擷取url屬性的值
      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.");
      }
      //讀取配置檔案的值,封裝為Properties對象 加入到defaults 中。
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //從configuration中擷取variables
      Properties vars = configuration.getVariables();
      if (vars != null) {
      	//如果有值,添加到defaults中
        defaults.putAll(vars);
      }
      //将parser的variables設定為defaults
      parser.setVariables(defaults);
      //将configuration的variables設定為defaults
      configuration.setVariables(defaults);
    }
  }
           

  解析name屬性為logImpl的setting标簽

private void loadCustomLogImpl(Properties props) {
	//擷取logImpl屬性值,并且包裝為class對象,這裡的值可以不是一個類的完整限定名,而是一個别名
    Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
 	//将logImpl設定到configuration的logImpl屬性中
    configuration.setLogImpl(logImpl);
}
protected <T> Class<? extends T> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
  	   //看這裡
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
}
protected <T> Class<? extends T> resolveAlias(String alias) {	
	//看這裡
    return typeAliasRegistry.resolveAlias(alias);
}
public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      //先轉為小寫
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      //Configuration對象執行個體化的時候(構造函數的具體邏輯),向typeAliases這個map中,放入了許多内置的值。
      //比如typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
      if (typeAliases.containsKey(key)) {
        //如果别名集合中含有該值,從取到對應的類
        value = (Class<T>) typeAliases.get(key);
      } else {
		//沒有,則直接調用classForName擷取到class對象
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
}
public void setLogImpl(Class<? extends Log> logImpl) {
    if (logImpl != null) {
      //将logImpl設定到configuration的logImpl屬性中
      this.logImpl = logImpl;
      LogFactory.useCustomLogging(this.logImpl);
    }
}
public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
    setImplementation(clazz);
}
private static void setImplementation(Class<? extends Log> implClass) {
    try {
      //擷取該日志類的構造函數
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      Log log = candidate.newInstance(LogFactory.class.getName());
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      //把該日志構造函數設定到LogFactory的logConstructor屬性中
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
}
           

  typeAliases标簽解析

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      //擷取子标簽
      for (XNode child : parent.getChildren()) {
      	//擷取package标簽
        if ("package".equals(child.getName())) {
          //擷取name屬性,也就是包名
          String typeAliasPackage = child.getStringAttribute("name");
          //看這裡
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          //配置的不是package,直接配置的别名和類的方式
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            //擷取class對象
            Class<?> clazz = Resources.classForName(type);
            //别名沒有配置
            if (alias == null) {
              //找類上的Alias注解,注解也沒有,直接使用類的simpleName作為别名
              typeAliasRegistry.registerAlias(clazz);
            } else {
              //注冊别名和類的對應關系到typeAliases中
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
}
public void registerAliases(String packageName) {
	//Object類型
    registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    //找到該包下所有的class檔案
    //使用classLoader.loadClass擷取對應class,如果是superType類型
    //收集到resolverUtil的matches集合中
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    //獲得resolverUtil的matches集合的類
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for (Class<?> type : typeSet) {
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      //如果類的SimpleName不為空,不是接口 , 不是内部類
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
}
public ResolverUtil<T> find(Test test, String packageName) {
	//将配置的包名中的.替換為/
    String path = getPackagePath(packageName);

    try {
      //VFS.getInstance(),靜态内部類的方式實作的單例模式
      //VFS主要用來讀取目前應用程式的資源檔案,預設有JBoss6VFS和DefaultVFS,loadCustomVfs方法會添加其他VFS的實作類
      //找到所有的資源檔案
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        //如果以.class結尾
        if (child.endsWith(".class")) {
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
}
protected void addIfMatching(Test test, String fqn) {
    try {
      //轉換為類的完整限定名
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
      if (log.isDebugEnabled()) {
        log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
      }
	
	  //使用類加載器加載該類
      Class<?> type = loader.loadClass(externalName);
      //使用的test為new ResolverUtil.IsA
      //對應的matches為
      //public boolean matches(Class<?> type) {
      //  return type != null && parent.isAssignableFrom(type);
      //}
      if (test.matches(type)) {
        //添加到matches容器中
        matches.add((Class<T>) type);
      }
    } catch (Throwable t) {
    //加載不到,忽略
      log.warn("Could not examine class '" + fqn + "'" + " due to a " +
          t.getClass().getName() + " with message: " + t.getMessage());
    }
}
public void registerAlias(Class<?> type) {
	//alias為類的simpleName
    String alias = type.getSimpleName();
    //擷取類的Alias注解的值
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    //注解不為空alias為注解配置的值
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    }
    //注冊别名和類的對應關系到typeAliases中
    registerAlias(alias, type);
}
public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    //将别名傳為小寫
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    //放入typeAliases(map)中
    typeAliases.put(key, value);
}
           

  解析plugins标簽

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {	
      //擷取子标簽
      for (XNode child : parent.getChildren()) {
      	//擷取interceptor屬性值(類名或者别名都可以)
        String interceptor = child.getStringAttribute("interceptor");
        //擷取child 的 子标簽的name和value值
        Properties properties = child.getChildrenAsProperties();
        //執行個體化攔截器類
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //調用攔截器的setProperties 入參為 properties
        interceptorInstance.setProperties(properties);
        //将攔截器添加到configuration的interceptorChain的interceptors集合中
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
           

  解析environments标簽

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
      	//擷取default屬性指派給environment屬性
        environment = context.getStringAttribute("default");
      }
      //循環所有的子标簽environment
      for (XNode child : context.getChildren()) {
      	//擷取子标簽environment的id屬性
        String id = child.getStringAttribute("id");
        //如果id和environment相等
        if (isSpecifiedEnvironment(id)) {
          //解析environment标簽的transactionManager标簽
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
           //解析environment标簽的dataSource标簽
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //設定txFactory,dataSource和id到environmentBuilder中
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          //将environmentBuilder.build()指派給configuration的environment屬性
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      //擷取type屬性,事務管理器的别名或者類名
      String type = context.getStringAttribute("type");
      //擷取子标簽的name和value屬性封裝為Properties 
      Properties props = context.getChildrenAsProperties();
      //執行個體化事務管理器
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      //設定props的值到TransactionFactory 中
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
}  
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      //擷取type屬性,資料源工廠的别名或者類名
      String type = context.getStringAttribute("type");
      //擷取子标簽的name和value屬性封裝為Properties 
      Properties props = context.getChildrenAsProperties();
      //執行個體化資料源工廠
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      //設定props的值到DataSourceFactory 中
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
           

  解析databaseIdProvider标簽

private void databaseIdProviderElement(XNode context) throws Exception {
	//<databaseIdProvider type="DB_VENDOR">
    //    <property name="Oracle" value="oracle"/>
    //    <property name="MySQL" value="mysql"/>
    //</databaseIdProvider>
    //oracle mysql這種值用于配置到mapper.xml中的databaseId屬性中.
    
    DatabaseIdProvider databaseIdProvider = null;
    if (context != null) {
      //擷取	type屬性值
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      //如果配置的是VENDOR,則轉為DB_VENDOR,都會用VendorDatabaseIdProvider類
      //typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
      if ("VENDOR".equals(type)) {
        type = "DB_VENDOR";
      }
      //擷取子标簽的name和value值封裝為properties 
      Properties properties = context.getChildrenAsProperties();
      //執行個體化DatabaseIdProvider類型
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
      //設定properties屬性值到databaseIdProvider中
      databaseIdProvider.setProperties(properties);
    }
    //擷取configuration的environment屬性
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      //擷取目前資料源對象對應property中的value值
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      //設定value值到configuration的databaseId中
      configuration.setDatabaseId(databaseId);
    }
}
public String getDatabaseId(DataSource dataSource) {
    if (dataSource == null) {
      throw new NullPointerException("dataSource cannot be null");
    }
    try {
  	   //看這裡
      return getDatabaseName(dataSource);
    } catch (Exception e) {
      LogHolder.log.error("Could not get a databaseId from dataSource", e);
    }
    return null;
}
private String getDatabaseName(DataSource dataSource) throws SQLException {
	//擷取databaseProductName
    String productName = getDatabaseProductName(dataSource);
    if (this.properties != null) {
      //循環配置的所有的properties值
      for (Map.Entry<Object, Object> property : properties.entrySet()) {
      	//如果productName包含property的key,則傳回property的value
        if (productName.contains((String) property.getKey())) {
          return (String) property.getValue();
        }
      }
      // no match, return null
      return null;
    }
    return productName;
}
private String getDatabaseProductName(DataSource dataSource) throws SQLException {
    Connection con = null;
    try {
      //使用資料源對象擷取連接配接對象
      con = dataSource.getConnection();
      DatabaseMetaData metaData = con.getMetaData();
      //擷取連接配接對象的DatabaseProductName , 比如 Oracle  MySQL 等
      return metaData.getDatabaseProductName();
    } finally {
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          // ignored
        }
      }
    }
}
           

  解析typeHandlers标簽

private void typeHandlerElement(XNode parent) throws Exception {
        if (parent != null) {
        	//擷取子标簽typeHandler或package
            for (XNode child : parent.getChildren()) {
            	//擷取package标簽
                if ("package".equals(child.getName())) {
                	//擷取package标簽的name屬性1
                    String typeHandlerPackage = child.getStringAttribute("name");
                    //掃描包,并且注冊typeHandle
                    typeHandlerRegistry.register(typeHandlerPackage);
                } else {
                	//處理子标簽typeHandler
                    String javaTypeName = child.getStringAttribute("javaType");
                    String jdbcTypeName = child.getStringAttribute("jdbcType");
                    String handlerTypeName = child.getStringAttribute("handler");
                    //可以配置别名,使用resolveClass擷取到具體的類
                    Class<?> javaTypeClass = resolveClass(javaTypeName);
                    JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
                    Class<?> typeHandlerClass = resolveClass(handlerTypeName);
                    //注冊javaType和Map<JdbcType, TypeHandler<?>> map(jdbcType和TypeHandler的映射關系)的映射關系
                    if (javaTypeClass != null) {
                        if (jdbcType == null) {
                            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
                        } else {
                            typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
                        }
                    } else {
                        typeHandlerRegistry.register(typeHandlerClass);
                    }
                }
            }
        }
}
public void register(String packageName) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    //掃描擷取包路徑下的所有TypeHandler類型的class
    resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
    //擷取掃描到的所有的TypeHandler類
    Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
    for (Class<?> type : handlerSet) {
      //Ignore inner classes and interfaces (including package-info.java) and abstract classes
      if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
      	//注冊
        register(type);
      }
    }
}
public void register(Class<?> typeHandlerClass) {
    boolean mappedTypeFound = false;
    //擷取MappedTypes注解
    MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
    if (mappedTypes != null) {
      //循環MappedTypes注解的值,也就是對應的可以處理的java類型
      for (Class<?> javaTypeClass : mappedTypes.value()) {
        //注冊
        register(javaTypeClass, typeHandlerClass);
        mappedTypeFound = true;
      }
    }
    if (!mappedTypeFound) {
      //注解沒有值的一樣可以注冊
      register(getInstance(null, typeHandlerClass));
    }
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    //擷取MappedJdbcTypes注解
    MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
    if (mappedJdbcTypes != null) {
       //循環MappedJdbcTypes注解的值,也就是對應的可以處理的jdbc類型
      for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
        //注冊
        register(javaType, handledJdbcType, typeHandler);
      }
      if (mappedJdbcTypes.includeNullJdbcType()) {
         //注冊
        register(javaType, null, typeHandler);
      }
    } else {
       //注冊
      register(javaType, null, typeHandler);
    }
}
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      //使用javaType擷取到map
      Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
      if (map == null || map == NULL_TYPE_HANDLER_MAP) {
        map = new HashMap<>();
        //注冊javaType和Map<JdbcType, TypeHandler<?>> map的映射關系
        typeHandlerMap.put(javaType, map);
      }
      //注冊jdbcType和typeHndler的映射關系
      map.put(jdbcType, handler);
    }
    //建立TypeHandler的class和TypeHandler的映射關系
    allTypeHandlersMap.put(handler.getClass(), handler);
}
           

  解析mappers标簽

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      //擷取子标簽package或者mapper
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //擷取package标簽的name屬性
          String mapperPackage = child.getStringAttribute("name");
          //掃描包下的xml檔案,解析加入到configuration中
          //解析mapper的xml的代碼和注解裡面的指定的sql的代碼較為繁瑣這裡就不詳細展開了
          //主要需要了解到:
          //解析的resultMap标簽的内容會加入configuration的resultMaps(Map)中,key為namespace加resultMap标簽的id屬性
          //解析的sql内容最終包裝為MappedStatement,加入到configuration的mappedStatements(map)中,key為namespace加自己标簽的id屬性
          //将mapper的接口類型和生成的代理工廠類(MapperProxyFactory)注冊到Configuration的mapperRegistry的knownMappers屬性中(這個後續再詳細說)
          //knownMappers.put(type, new MapperProxyFactory<>(type));
          configuration.addMappers(mapperPackage);
        } else {
          //處理mapper标簽的屬性
          //讀取到配置的xml檔案
          //解析加入到configuration中
          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.");
          }
        }
      }
    }
}

public void parseStatementNode() {
	//省略部分源碼
	
	//解析的sql内容包裝為SqlSource 
	//一般有動态标簽的則為DynamicSqlSource
	//DynamicSqlSource含有configuration和rootSqlNode(解析出的sql内容,包裝為了MixedSqlNode,裡面持有其他的sqlNode節點,樹狀結構嵌套各個sqlNode(标簽中包含sql或者換行空格或者其他标簽))
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    
    //省略部分源碼
	
	//解析的sql包裝為MappedStatement,然後加入到configuration中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
           

  到此解析mybatis的xml配置檔案的主要流程就結束了。

繼續閱讀