• 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 ---------------------------------------------------------------------------------------------------------------------------------------------

     
     
     
     
     


    生于忧患,死于安乐
  • 相关阅读:
    删除文件时,提示 "操作无法完成..." 怎么处理
    对象的理解
    TP5架构下链接SQL数据库的一种方法
    关于URL隐藏index.php方法
    非典型的千万用户后台之路
    就这样,再见2015
    理想的程序员
    4个小例子告诉你:如何成为一名数据极客
    馆中窥职:小公司没那么糟糕
    JAVA设计模式详解(六)----------状态模式
  • 原文地址:https://www.cnblogs.com/songlove/p/14607610.html
Copyright © 2020-2023  润新知