在<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 一文中,我们知道mybatis配置文件是由XMLConfigBuilder来解析的,看以下代码:

  1. public Configuration parse() {
    if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
    }
  2.  
  3. 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"));
    //解析mapper
    mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
    }

可以看到解析mapper文件的加载解析i是从 mapperElement(root.evalNode("mappers"));  开始处理的。

我们继续看mapperElement(XNode parent)方法的代码:

  1. private void mapperElement(XNode parent) throws Exception {
  2. if (parent != null) {
  3. for (XNode child : parent.getChildren()) {
  4. if ("package".equals(child.getName())) { //解析package扫描指定package下的所有mapper接口
  5. String mapperPackage = child.getStringAttribute("name");
  6. configuration.addMappers(mapperPackage);
  7. } else { //解析mapper节点
  8. String resource = child.getStringAttribute("resource");
  9. String url = child.getStringAttribute("url");
  10. String mapperClass = child.getStringAttribute("class");
  11. if (resource != null && url == null && mapperClass == null) {
  12. ErrorContext.instance().resource(resource);
  13. InputStream inputStream = Resources.getResourceAsStream(resource);
  14. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
  15. mapperParser.parse();
  16. } else if (resource == null && url != null && mapperClass == null) {
  17. ErrorContext.instance().resource(url);
  18. InputStream inputStream = Resources.getUrlAsStream(url);
  19. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
  20. mapperParser.parse();
  21. } else if (resource == null && url == null && mapperClass != null) {
  22. Class<?> mapperInterface = Resources.classForName(mapperClass);
  23. configuration.addMapper(mapperInterface);
  24. } else {
  25. throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
  26. }
  27. }
  28. }
  29. }
  30. }

可以看到,mybatisConfig.xml配置下的mappers节点有2种子节点:package节点和mapper节点,我这里先讨论xml的模式,先看mapper节点。

mapper节点配置有3个属性:resource,url,class。他们处理的优先级依次是resource,url,class,3个属性只处理一种。resource,url属性映射的是xml的路径,class是mapper接口的类路径。

从源码中我们看到,通过读取resource或url属性得到xml的访问路径后,交给XMLMapperBuilder对象来解析。

我们查看XMLMapperBuilder的parse方法:

  1. public void parse() {
  2. if (!configuration.isResourceLoaded(resource)) {
  3. configurationElement(parser.evalNode("/mapper")); //从mapper节点开始解析
  4. configuration.addLoadedResource(resource); //标记已经加载了次xml资源
  5. bindMapperForNamespace(); //绑定到命名空间
  6. }
  7.  
  8. parsePendingResultMaps(); //将resultMap映射信息转换成ResultMap对象
  9. parsePendingChacheRefs(); //将cache映射信息转换成Cache对象
  10. parsePendingStatements(); //将sql映射转换成MappedStatement
  11. }
  12.  
  13. //解析xml
    private void configurationElement(XNode context) {
  1. try {
    String namespace = context.getStringAttribute("namespace");
    if (namespace.equals("")) {
    throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
    }
  1. //绑定到命名空间
    private void bindMapperForNamespace() {
  1. String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
    Class<?> boundType = null;
    try {
    boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
    //ignore, bound type is not required
    }
    if (boundType != null) {
    if (!configuration.hasMapper(boundType)) {
    // Spring may not know the real resource name so we set a flag
    // to prevent loading again this resource from the mapper interface
    // look at MapperAnnotationBuilder#loadXmlResource
    configuration.addLoadedResource("namespace:" + namespace);
    configuration.addMapper(boundType);
    }
    }
    }
    }
  1.  

其中configurationElement(XNode context)负责解析所有的xml元素,bindMapperForNamespace() 绑定到命名空间

而parsePendingResultMaps(), parsePendingChacheRefs(), parsePendingStatements()则分别将对应的xml信息转换成ResultMap对象,Cache对象和MappedStatement对象。

 ResultMap的处理

  1. private void parsePendingResultMaps() {
  2. Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
  3. synchronized (incompleteResultMaps) {
  4. Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
  5. while (iter.hasNext()) {
  6. try {
  7. iter.next().resolve(); //实际处理的是ResultMapResolver.resolve()方法
  8. iter.remove();
  9. } catch (IncompleteElementException e) {
  10. // ResultMap is still missing a resource...
  11. }
  12. }
  13. }
  14. }

看代码得知,实际处理的是ResultMapResolver.resolve()方法

  1. package org.apache.ibatis.builder;
  2.  
  3. import java.util.List;
  4.  
  5. import org.apache.ibatis.mapping.Discriminator;
  6. import org.apache.ibatis.mapping.ResultMap;
  7. import org.apache.ibatis.mapping.ResultMapping;
  8.  
  9. /**
  10. * @author Eduardo Macarron
  11. */
  12. public class ResultMapResolver {
  13. private final MapperBuilderAssistant assistant;
  14. private String id;
  15. private Class<?> type;
  16. private String extend;
  17. private Discriminator discriminator;
  18. private List<ResultMapping> resultMappings;
  19. private Boolean autoMapping;
  20.  
  21. public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) {
  22. this.assistant = assistant;
  23. this.id = id;
  24. this.type = type;
  25. this.extend = extend;
  26. this.discriminator = discriminator;
  27. this.resultMappings = resultMappings;
  28. this.autoMapping = autoMapping;
  29. }
  30.  
  31. public ResultMap resolve() {
  32. return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
  33. }
  34.  
  35. }

接着发现最终调用的是MapperBuilderAssistant.addResultMap 方法

  1. public ResultMap addResultMap(
  2. String id,
  3. Class<?> type,
  4. String extend,
  5. Discriminator discriminator,
  6. List<ResultMapping> resultMappings,
  7. Boolean autoMapping) {
  8. id = applyCurrentNamespace(id, false);
  9. extend = applyCurrentNamespace(extend, true);
  10.  
  11. ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);
  12. if (extend != null) {
  13. if (!configuration.hasResultMap(extend)) {
  14. throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
  15. }
  16. ResultMap resultMap = configuration.getResultMap(extend);
  17. List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
  18. extendedResultMappings.removeAll(resultMappings);
  19. // Remove parent constructor if this resultMap declares a constructor.
  20. boolean declaresConstructor = false;
  21. for (ResultMapping resultMapping : resultMappings) {
  22. if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
  23. declaresConstructor = true;
  24. break;
  25. }
  26. }
  27. if (declaresConstructor) {
  28. Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
  29. while (extendedResultMappingsIter.hasNext()) {
  30. if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
  31. extendedResultMappingsIter.remove();
  32. }
  33. }
  34. }
  35. resultMappings.addAll(extendedResultMappings);
  36. }
  37. resultMapBuilder.discriminator(discriminator);
  38. ResultMap resultMap = resultMapBuilder.build();
  39. configuration.addResultMap(resultMap); //添加到Configuration
  40. return resultMap;
  41. }

Cache的处理

  1. private void parsePendingChacheRefs() {
  2. Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
  3. synchronized (incompleteCacheRefs) {
  4. Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
  5. while (iter.hasNext()) {
  6. try {
  7. iter.next().resolveCacheRef();
  8. iter.remove();
  9. } catch (IncompleteElementException e) {
  10. // Cache ref is still missing a resource...
  11. }
  12. }
  13. }
  14. }

调用的是org.apache.ibatis.builder.CacheRefResolver.resolveCacheRef()方法:

  1. public Cache resolveCacheRef() {
  2. return assistant.useCacheRef(cacheRefNamespace);
  3. }

最终调用org.apache.ibatis.builder.useCacheRef(String namespace)方法创建Cache对象并添加到Configuration:

  1. public Cache useCacheRef(String namespace) {
  2. if (namespace == null) {
  3. throw new BuilderException("cache-ref element requires a namespace attribute.");
  4. }
  5. try {
  6. unresolvedCacheRef = true;
  7. Cache cache = configuration.getCache(namespace);
  8. if (cache == null) {
  9. throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
  10. }
  11. currentCache = cache;
  12. unresolvedCacheRef = false;
  13. return cache;
  14. } catch (IllegalArgumentException e) {
  15. throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
  16. }
  17. }

MappedStatement的处理

从parsePendingStatements()方法开始跟踪

  1. private void parsePendingStatements() {
  2. Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
  3. synchronized (incompleteStatements) {
  4. Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
  5. while (iter.hasNext()) {
  6. try {
  7. iter.next().parseStatementNode(); //调用XMLStatementBuilder的parseStatementNode方法
  8. iter.remove();
  9. } catch (IncompleteElementException e) {
  10. // Statement is still missing a resource...
  11. }
  12. }
  13. }
  14. }

然后调用org.apache.ibatis.builder.xml.XMLStatementBuilder的parseStatementNode方法

  1. public void parseStatementNode() {
  2. String id = context.getStringAttribute("id");
  3. String databaseId = context.getStringAttribute("databaseId");
  4.  
  5. if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
  6.  
  7. Integer fetchSize = context.getIntAttribute("fetchSize");
  8. Integer timeout = context.getIntAttribute("timeout");
  9. String parameterMap = context.getStringAttribute("parameterMap");
  10. String parameterType = context.getStringAttribute("parameterType");
  11. Class<?> parameterTypeClass = resolveClass(parameterType);
  12. String resultMap = context.getStringAttribute("resultMap");
  13. String resultType = context.getStringAttribute("resultType");
  14. String lang = context.getStringAttribute("lang");
  15. LanguageDriver langDriver = getLanguageDriver(lang);
  16.  
  17. Class<?> resultTypeClass = resolveClass(resultType);
  18. String resultSetType = context.getStringAttribute("resultSetType");
  19. StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  20. ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  21.  
  22. String nodeName = context.getNode().getNodeName();
  23. SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  24. boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  25. boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  26. boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  27. boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
  28.  
  29. // Include Fragments before parsing
  30. XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  31. includeParser.applyIncludes(context.getNode());
  32.  
  33. // Parse selectKey after includes and remove them.
  34. processSelectKeyNodes(id, parameterTypeClass, langDriver);
  35.  
  36. // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  37. SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  38. String resultSets = context.getStringAttribute("resultSets");
  39. String keyProperty = context.getStringAttribute("keyProperty");
  40. String keyColumn = context.getStringAttribute("keyColumn");
  41. KeyGenerator keyGenerator;
  42. String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  43. keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  44. if (configuration.hasKeyGenerator(keyStatementId)) {
  45. keyGenerator = configuration.getKeyGenerator(keyStatementId);
  46. } else {
  47. keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
  48. configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
  49. ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
  50. }
  51.  
  52. builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
  53. fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
  54. resultSetTypeEnum, flushCache, useCache, resultOrdered,
  55. keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  56. }

最后MappedStatement由org.apache.ibatis.builder.MapperBuilderAssistant的addMappedStatement方法创建,并加入到Configuration

  1. public MappedStatement addMappedStatement(
  2. String id,
  3. SqlSource sqlSource,
  4. StatementType statementType,
  5. SqlCommandType sqlCommandType,
  6. Integer fetchSize,
  7. Integer timeout,
  8. String parameterMap,
  9. Class<?> parameterType,
  10. String resultMap,
  11. Class<?> resultType,
  12. ResultSetType resultSetType,
  13. boolean flushCache,
  14. boolean useCache,
  15. boolean resultOrdered,
  16. KeyGenerator keyGenerator,
  17. String keyProperty,
  18. String keyColumn,
  19. String databaseId,
  20. LanguageDriver lang,
  21. String resultSets) {
  22.  
  23. if (unresolvedCacheRef) throw new IncompleteElementException("Cache-ref not yet resolved");
  24.  
  25. id = applyCurrentNamespace(id, false);
  26. boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  27.  
  28. MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
  29. statementBuilder.resource(resource);
  30. statementBuilder.fetchSize(fetchSize);
  31. statementBuilder.statementType(statementType);
  32. statementBuilder.keyGenerator(keyGenerator);
  33. statementBuilder.keyProperty(keyProperty);
  34. statementBuilder.keyColumn(keyColumn);
  35. statementBuilder.databaseId(databaseId);
  36. statementBuilder.lang(lang);
  37. statementBuilder.resultOrdered(resultOrdered);
  38. statementBuilder.resulSets(resultSets);
  39. setStatementTimeout(timeout, statementBuilder);
  40.  
  41. setStatementParameterMap(parameterMap, parameterType, statementBuilder);
  42. setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
  43. setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
  44.  
  45. MappedStatement statement = statementBuilder.build();
  46. configuration.addMappedStatement(statement);
  47. return statement;
  48. }

Mapper  xml部分的解析,暂时粗略的写这么多,后续我们还将讲解package包的扫描和指定class的mapper的情况。

MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件的更多相关文章

  1. MyBatis框架的使用及源码分析(六) MapperRegistry

    我们先Mapper接口的调用方式,见<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用>的示例: public void findUserById() { Sql ...

  2. MyBatis框架的使用及源码分析(七) MapperProxy,MapperProxyFactory

    从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过Ma ...

  3. MyBatis框架的使用及源码分析(十一) StatementHandler

    我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Ex ...

  4. MyBatis框架的使用及源码分析(九) Executor

    从<MyBatis框架的使用及源码分析(八) MapperMethod>文中我们知道执行Mapper的每一个接口方法,最后调用的是MapperMethod.execute方法.而当执行Ma ...

  5. MyBatis框架的使用及源码分析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder

    在 <MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 的demo中看到了SessionFactory的创建过程: SqlSessionFactory sess ...

  6. MyBatis框架的使用及源码分析(八) MapperMethod

    从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...

  7. MyBatis框架的使用及源码分析(五) DefaultSqlSessionFactory和DefaultSqlSession

    我们回顾<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 一文的示例 private static SqlSessionFactory getSessionF ...

  8. MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor

    Executor分成两大类,一类是CacheExecutor,另一类是普通Executor. 普通类又分为: ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情.它为每个语句的执行 ...

  9. MyBatis框架的使用及源码分析(三) 配置篇 Configuration

    从上文<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 我们知道XMLConf ...

随机推荐

  1. 在cmd里面使用mysql命令

    1.先找出mysqld文件所在的位置,我的是在C:\Program Files\MySQL\MySQL Server 5.1\bin. 2.cd C:\Program Files\MySQL\MySQ ...

  2. lintcode-172-删除元素

    172-删除元素 给定一个数组和一个值,在原地删除与值相同的数字,返回新数组的长度. 元素的顺序可以改变,并且对新的数组不会有影响. 样例 给出一个数组 [0,4,4,0,0,2,4,4],和值 4 ...

  3. <Android>tab选项卡

    1.继承TabActivity实现 a)         在布局文件中使用FrameLayout列出Tab组件及Tab中的内容组件 b)        Activity要继承TabActivity c ...

  4. 【week2】 四则运算改进

    四则运算满足简单加减乘除,以及包含括号的复杂四则运算. 代码描述: 1.采用random随机数产生要参与计算的数字,以及运算符号 2.采用Scanner获取控制台输入的结果,与计算出来的结果进行比对, ...

  5. Web服务器性能压力测试工具

    一.http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载. 但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般 ...

  6. PHP给图片添加图片水印

    涉及到的函数: 1.file_get_contents():用于将文件的内容读入到一个字符串中的首选方法.如果操作系统支持,还会使用内存映射技术来增强性能. 2.list():list() 函数用于在 ...

  7. Android基础------Intent组件

    1.什么是intent 同Activity一样,也是Android应用组件在Android中承担着一种指令输出的作用Intent负责对应用中一次操作的动作及动作相关的数据进行描述.Android则根据 ...

  8. vs code 自动补全效果不理想的问题

    之前一直用webstorm,最近换换口味,改用了VS Code,发现VS Code 智能提示得到的都不是我想要的 就比如  ! + tab ,HTML结构都出不来.经过一番搜索,发现是 VS Code ...

  9. 反向传播算法 Backpropagation Algorithm

    假设我们有一个固定样本集,它包含 个样例.我们可以用批量梯度下降法来求解神经网络.具体来讲,对于单个样例(x,y),其代价函数为:这是一个(二分之一的)方差代价函数.给定一个包含 个样例的数据集,我们 ...

  10. bzoj 4568 [SCOI 2016] 幸运数字

    题目大意 给定一棵\(n\)个点的树,每个点有权值 \(q\)次询问树上路径中 每个点权值可选可不选的最大异或和 \(n\le 2*10^4,q\le 2*10^5,val[i]\le 2^{60}\ ...