Mybaits源码分析七之XMLConfigBuilder类mappers标签解析

根据mybatis配置加载流程 

 1 private void parseConfiguration(XNode root) {
 2     try {
 3       //解析子节点的properties文件
 4       propertiesElement(root.evalNode("properties"));
 5       //加载用户自定义配置
 6       Properties settings = settingsAsProperties(root.evalNode("settings"));
 7       //加载vfs虚拟文件系统配置
 8       loadCustomVfs(settings);
 9      // 解析子节点typeAliases 别名
10       typeAliasesElement(root.evalNode("typeAliases"));
11       //解析子节点plugins 插件
12       pluginElement(root.evalNode("plugins"));
13       //解析子节点objectFactory mybatis为结果创建对象时都会用到objectFactory
14       objectFactoryElement(root.evalNode("objectFactory"));
15       //解析子节点的封装对象
16       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
17       reflectorFactoryElement(root.evalNode("reflectorFactory"));
18       //解析settings标签的文件配置
19       settingsElement(settings);
20       //配置运行环境
21       environmentsElement(root.evalNode("environments"));
22       //解析子节点配置数据库供应商
23       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
24       //解析对象类型处理器,处理Java的pojo类型与数据库类型的映射
25       typeHandlerElement(root.evalNode("typeHandlers"));
26       //解析子节点的mappers
27       mapperElement(root.evalNode("mappers"));
28     } catch (Exception e) {
29       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
30     }
31   }
 mapperElement(root.evalNode("mappers"));主要解决mappers标签的配置解析,在mybatis中<mappers>写法为:
 1 <!--加载mapper映射 如果将和spring整合后,可以使用整合包中提供的mapper扫描器,此处的mappers不用配置了。 -->
 2  <mappers>
 3 <!-- 通过resource引用mapper的映射文件 -->
 4         <!--<mapper resource="com/ssc/demo/mybatis/mapper/SubjectMapper.xml"/>-->
 5 <!-- <mapper resource="mapper/UserMapper.xml" /> -->
 6         <!--<mapper url="file:///D:workspaceideagitdemo_01srcmainjavacomsscdemomybatismapperSubjectMapper.xml"/>-->
 7 <!-- 通过class引用mapper接口 class:配置mapper接口全限定名 要求:需要mapper.xml和mapper.java同名并且在一个目录 
 8             中 -->
 9         <!--<mapper class="com.ssc.demo.mybatis.dao.SubjectDao"/>-->
10 <!-- 批量mapper配置 通过package进行自动扫描包下边的mapper接口, 要求:需要mapper.xml和mapper.java同名并且在一个目录 
11             中 -->
12         <package name="com.ssc.demo.mybatis.mapper"/>
13     </mappers>
 mapperElement(root.evalNode("mappers"));方法源码为:
 1 private void mapperElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4      //package扫描方式
 5         if ("package".equals(child.getName())) {
 6           String mapperPackage = child.getStringAttribute("name");
 7           configuration.addMappers(mapperPackage);
 8         } else {
 9           String resource = child.getStringAttribute("resource");
10           String url = child.getStringAttribute("url");
11           String mapperClass = child.getStringAttribute("class");
12           if (resource != null && url == null && mapperClass == null) {
13 //resource方式
14             ErrorContext.instance().resource(resource);
15             InputStream inputStream = Resources.getResourceAsStream(resource);
16             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
17             mapperParser.parse();
18           } else if (resource == null && url != null && mapperClass == null) {
19 //URL方式
20             ErrorContext.instance().resource(url);
21             InputStream inputStream = Resources.getUrlAsStream(url);
22             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
23             mapperParser.parse();
24           } else if (resource == null && url == null && mapperClass != null) {
25      //class 方式
26             Class<?> mapperInterface = Resources.classForName(mapperClass);
27             configuration.addMapper(mapperInterface);
28           } else {
29             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
30           }
31         }
32       }
33     }
34   }
通过以上方法得知,四种方式对应两种不同形式的mapper配置解析分别为configuration.addMappers(mapperPackage);和 mapperParser.parse();
configuration.addMappers(mapperPackage)源码为:
1  public <T> void addMapper(Class<T> type) {
2     mapperRegistry.addMapper(type);
3   }
 1 public <T> void addMapper(Class<T> type) {
 2    //判断这类是否是接口类型
 3     if (type.isInterface()) {
 4    //判断是否已加载
 5       if (hasMapper(type)) {
 6         throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
 7       }
 8       boolean loadCompleted = false;
 9       try {
10         knownMappers.put(type, new MapperProxyFactory<T>(type));
11         // It's important that the type is added before the parser is run
12         // otherwise the binding may automatically be attempted by the
13         // mapper parser. If the type is already known, it won't try.
14       //构造一个MapperAnnotationBuilder对象解析这个类
15         MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
16         parser.parse();
17         loadCompleted = true;
18       } finally {
19         if (!loadCompleted) {
20           knownMappers.remove(type);
21         }
22       }
23     }
24   }
parser.parse();方法源码为:
 1 public void parse() {
 2     String resource = type.toString();
 3 //判断是否已加载此  配置的例如(USERDAO) 接口
 4     if (!configuration.isResourceLoaded(resource)) {
 5 //  加载与此类同名的xml类型mapper文件
 6       loadXmlResource();
 7 //将该资源加入到全局配置
 8       configuration.addLoadedResource(resource);
 9 //设置当前命名空间
10       assistant.setCurrentNamespace(type.getName());
11 //解析二级缓存
12       parseCache();
13       parseCacheRef();
14    //获取类中的所以的方法
15       Method[] methods = type.getMethods();
16       for (Method method : methods) {
17         try {
18           // 判断该方法是否是桥接方法,桥接方法指的是由编译器自动生成的方法
19           if (!method.isBridge()) {
20         //解析statement
21             parseStatement(method);
22           }
23         } catch (IncompleteElementException e) {
24 //将异常解析的方法加入一个List中
25           configuration.addIncompleteMethod(new MethodResolver(this, method));
26         }
27       }
28     }
29 //解析挂起的方法
30     parsePendingMethods();
31   }
  loadXmlResource();是加载同级包下面的同名的xml配置文件,其源码为
 1   private void loadXmlResource() {
 2     // 判断此资源是否已经加载,防止加载两次
 3     if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
 4 //将此类的全限类名的路径替换
 5       String xmlResource = type.getName().replace('.', '/') + ".xml";
 6       InputStream inputStream = null;
 7       try {
 8 //加载同名的xml类型的mapper文件
 9         inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
10       } catch (IOException e) {
11         // ignore, resource is not required
12       }
13       if (inputStream != null) {
14         XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
15       //xml文件解析
16         xmlParser.parse();
17       }
18     }
19   }
 xmlParser.parse();是对mapper.xml文件解析。源码分析为
--------------------------------------------------------
start xmlParser----------------------------------------------------------------------------------

 1  public void parse() {
 2    //判断该资源是否已加载
 3     if (!configuration.isResourceLoaded(resource)) {
 4     //加载mapper.xml文件下的mapper节点
 5       configurationElement(parser.evalNode("/mapper"));
 6     //加载配置
 7       configuration.addLoadedResource(resource);
 8    //mapper绑定命名空间
 9       bindMapperForNamespace();
10     }
11     //解析待定的ResultMaps
12     parsePendingResultMaps();
13    //解析待定的缓存
14     parsePendingCacheRefs();
15    //解析待定的Statements
16     parsePendingStatements();
17   }

 configurationElement(parser.evalNode("/mapper"));源码为

 1   private void configurationElement(XNode context) {
 2     try {
 3       String namespace = context.getStringAttribute("namespace");
 4       if (namespace == null || namespace.equals("")) {
 5         throw new BuilderException("Mapper's namespace cannot be empty");
 6       }
 7 //设置命名空间
 8       builderAssistant.setCurrentNamespace(namespace);
 9       //引用其他namespace的二级缓存
10       cacheRefElement(context.evalNode("cache-ref"));
11      //引用当前namespace的二级缓存
12       cacheElement(context.evalNode("cache"));
13     //parameterMap节点解析     
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
14 //resultMap节点解析 15 resultMapElements(context.evalNodes("/mapper/resultMap")); 16 //sql片段节点解析 17 sqlElement(context.evalNodes("/mapper/sql")); 18 //构建statement 19 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); 20 } catch (Exception e) { 21 throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); 22 } 23 }
 1 /**
 2    *主要将parameterMap节点下的各参数值获取,得到 
 3  *ParameterMapping对象,再放到parameterMappings列表中
 4 */
 5 private void parameterMapElement(List<XNode> list) throws Exception {
 6     for (XNode parameterMapNode : list) {
 7       String id = parameterMapNode.getStringAttribute("id");
 8       String type = parameterMapNode.getStringAttribute("type");
 9       Class<?> parameterClass = resolveClass(type);
10       List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
11       List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
12       for (XNode parameterNode : parameterNodes) {
13         String property = parameterNode.getStringAttribute("property");
14         String javaType = parameterNode.getStringAttribute("javaType");
15         String jdbcType = parameterNode.getStringAttribute("jdbcType");
16         String resultMap = parameterNode.getStringAttribute("resultMap");
17         String mode = parameterNode.getStringAttribute("mode");
18         String typeHandler = parameterNode.getStringAttribute("typeHandler");
19         Integer numericScale = parameterNode.getIntAttribute("numericScale");
20         ParameterMode modeEnum = resolveParameterMode(mode);
21         Class<?> javaTypeClass = resolveClass(javaType);
22         JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
23         @SuppressWarnings("unchecked")
24         Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
25         ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
26         parameterMappings.add(parameterMapping);
27       }
28       builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
29     }
30   }
 1  private void resultMapElements(List<XNode> list) throws Exception {
 2     for (XNode resultMapNode : list) {
 3       try {
 4         resultMapElement(resultMapNode);
 5       } catch (IncompleteElementException e) {
 6         // ignore, it will be retried
 7       }
 8     }
 9   }
10 
11   private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
12     return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
13   }
14 
15   private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
16     ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
17     String id = resultMapNode.getStringAttribute("id",
18         resultMapNode.getValueBasedIdentifier());
19     String type = resultMapNode.getStringAttribute("type",
20         resultMapNode.getStringAttribute("ofType",
21             resultMapNode.getStringAttribute("resultType",
22                 resultMapNode.getStringAttribute("javaType"))));
23     String extend = resultMapNode.getStringAttribute("extends");
24     Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
25     Class<?> typeClass = resolveClass(type);
26     Discriminator discriminator = null;
27     List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
28     resultMappings.addAll(additionalResultMappings);
29     List<XNode> resultChildren = resultMapNode.getChildren();
30     for (XNode resultChild : resultChildren) {
31       if ("constructor".equals(resultChild.getName())) {
32         processConstructorElement(resultChild, typeClass, resultMappings);
33       } else if ("discriminator".equals(resultChild.getName())) {
34         discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
35       } else {
36         List<ResultFlag> flags = new ArrayList<ResultFlag>();
37         if ("id".equals(resultChild.getName())) {
38           flags.add(ResultFlag.ID);
39         }
40         resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
41       }
42     }
43     ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
44     try {
45       return resultMapResolver.resolve();
46     } catch (IncompleteElementException  e) {
47       configuration.addIncompleteResultMap(resultMapResolver);
48       throw e;
49     }
50   }
 1  private void sqlElement(List<XNode> list) throws Exception {
 2     if (configuration.getDatabaseId() != null) {
 3       sqlElement(list, configuration.getDatabaseId());
 4     }
 5     sqlElement(list, null);
 6   }
 7 
 8   private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
 9     for (XNode context : list) {
10       String databaseId = context.getStringAttribute("databaseId");
11       String id = context.getStringAttribute("id");
12       id = builderAssistant.applyCurrentNamespace(id, false);
13       if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
14         sqlFragments.put(id, context);
15       }
16     }
17   }
 1  public void parseStatementNode() {
 2     String id = context.getStringAttribute("id");
 3     String databaseId = context.getStringAttribute("databaseId");
 4 
 5     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
 6       return;
 7     }
 8      //指的是一次获取结果数量大小
 9     Integer fetchSize = context.getIntAttribute("fetchSize");
10     Integer timeout = context.getIntAttribute("timeout");
11     String parameterMap = context.getStringAttribute("parameterMap");
12     String parameterType = context.getStringAttribute("parameterType");
13     Class<?> parameterTypeClass = resolveClass(parameterType);
14     String resultMap = context.getStringAttribute("resultMap");
15     String resultType = context.getStringAttribute("resultType");
16     String lang = context.getStringAttribute("lang");
17     LanguageDriver langDriver = getLanguageDriver(lang);
18     Class<?> resultTypeClass = resolveClass(resultType);
19     String resultSetType = context.getStringAttribute("resultSetType");
20     //判断标签中是否指定了statementType参数,如果没有默认为预编译,主要有三种 STATEMENT, PREPARED, CALLABLE选择
21     StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
22     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
23     //获取节点名称
24     String nodeName = context.getNode().getNodeName();
25     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
26     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
27     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
28     boolean useCache = context.getBooleanAttribute("useCache", isSelect);
29     boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
30 
31     // Include Fragments before parsing 先解析sql片段
32     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
33     includeParser.applyIncludes(context.getNode());
34 
35     // Parse selectKey after includes and remove them.
36     processSelectKeyNodes(id, parameterTypeClass, langDriver);
37     
38     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed),创建sql资源(包含了sql语句,参数,配置)
39     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
40     String resultSets = context.getStringAttribute("resultSets");
41     String keyProperty = context.getStringAttribute("keyProperty");
42     String keyColumn = context.getStringAttribute("keyColumn");
43     KeyGenerator keyGenerator;
44     //statementId 类型为“com.ssc.demo.mybatis.dao.SubjectDao.selectSubjects!selectKey”
45     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
46     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
47     if (configuration.hasKeyGenerator(keyStatementId)) {
48       keyGenerator = configuration.getKeyGenerator(keyStatementId);
49     } else {
50       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
51           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
52           ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
53     }
54 
55     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
56         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
57         resultSetTypeEnum, flushCache, useCache, resultOrdered, 
58         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
59   }

--------------------------------------------------------------------------------------------------end ---------------------------------------------------------------------------------------------------------------------------------------------