Mybatis之Xml解析说明

开局一张图,后面全靠编。Mybatis结构图:


在这张图里面,我们看到configuration在整个Mybatis里面到处都有,跟黄金一样成为必不可少的元素。

这张是整个流程结构的图,从网上找的。


入口在SqlSessionFactoryBuilder#build方法:

//代码从这里开始执行
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    //在这里创建xml建造器,并解析xml
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    //解析并构建 并使用DefaultSqlSessionFactoryConfiguration    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.
    }
  }
}

先进入XMLConfigBuilder构造里面,我们看到super(new Configuration());这里就是将jdbc jndi这些类注册到typeAliasRegistry中。然后在进入parse方法中,开始解析xml

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //jdbc jndi这些注册到typeAliasRegistry
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}
//解析xml里面的信息
public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //解析configuration里面的xml
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}
private void parseConfiguration(XNode root) {
    try { 
        //这里讲一下 environmentstypeAliasesmappers
//issue #117 read properties first propertiesElement(root.evalNode( "properties")) ; Properties settings = settingsAsProperties(root.evalNode( "settings")) ; loadCustomVfs(settings) ; typeAliasesElement(root.evalNode( "typeAliases")) ; pluginElement(root.evalNode( "plugins")) ; objectFactoryElement(root.evalNode( "objectFactory")) ; objectWrapperFactoryElement(root.evalNode( "objectWrapperFactory")) ; reflectorFactoryElement(root.evalNode( "reflectorFactory")) ; settingsElement(settings) ; // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode( "environments")) ; databaseIdProviderElement(root.evalNode( "databaseIdProvider")) ; typeHandlerElement(root.evalNode( "typeHandlers")) ; mapperElement(root.evalNode( "mappers")) ; } catch (Exception e) { throw new BuilderException( "Error parsing SQL Mapper Configuration. Cause: " + e , e) ; }} typeAliases

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
        // 遍历<typeAliases>下的所有子节点
        for (XNode child : parent.getChildren()) {
            // 若当前结点为<package>
            if ("package".equals(child.getName())) {
                // 获取<package>上的name属性(包名)
                String typeAliasPackage = child.getStringAttribute("name");
                // 为该包下的所有类起个别名,并注册进configurationtypeAliasRegistry                configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
            }
            // 如果当前结点为< typeAlias >
            else {
                // 获取aliastype属性
                String alias = child.getStringAttribute("alias");
                 String type = child.getStringAttribute("type");
                // 注册进configurationtypeAliasRegistry                try {
                    Class<?> clazz = Resources.classForName(type);
                    if (alias == null) {
                        typeAliasRegistry.registerAlias(clazz);
                    } else {
                        typeAliasRegistry.registerAlias(alias, clazz);
                    }
                } catch (ClassNotFoundException e) {
                    throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
                }
            }
        }
    }
}

environments

//获取环境
private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        if (environment == null) {
            environment = context.getStringAttribute("default");
        }
        //拿到环境的标识
        for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            //如果environmentid都为空时,不解析 直接跳出
            if (isSpecifiedEnvironment(id)) {
                //根据传入的type来创建不同的事务工厂 "jdbc" -> "class org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory"
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                //根据传入的type来创建不同的数据源工厂  "pooled" -> "org.apache.ibatis.datasource.pooled.PooledDataSourceFactory"
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                //获得连接池
                DataSource dataSource = dsFactory.getDataSource();
                Environment.Builder environmentBuilder = new Environment.Builder(id)
                        .transactionFactory(txFactory)
                        .dataSource(dataSource);
                //将事务工厂、连接池添加到环境中
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}

private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
        //获取    <transactionManager type="JDBC"/>
        String type = context.getStringAttribute("type");
        Properties props = context.getChildrenAsProperties();
        //从里面获取到对应的类
        TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
        factory.setProperties(props);
        return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
public <T> Class<T> resolveAlias(String string) {
  try {
    if (string == null) {
      return null;
    }
    // 转换为小写
    String key = string.toLowerCase(Locale.ENGLISH);
    Class<T> value;
    if (TYPE_ALIASES.containsKey(key)) {
      //从缓存从拿
      value = (Class<T>) TYPE_ALIASES.get(key);
    } else { 
      value = (Class<T>) Resources.classForName(string);
    }
    return value;
  } catch (ClassNotFoundException e) {
    throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  }
}

databaseIdProviderElement跟上面的流程一样,在这里就不重复了。处理完成后,把事务、连接池都加入configuration.setEnvironment(environmentBuilder.build());中。

mapper

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        // 遍历<mappers>下所有子节点
        for (XNode child : parent.getChildren()) {
            // 如果当前节点为<package>
            if ("package".equals(child.getName())) {
                // 获取<package>name属性(该属性值为mapper class所在的包名)
                String mapperPackage = child.getStringAttribute("name");
                // 将该包下的所有Mapper Class注册到configurationmapperRegistry容器中
                configuration.addMappers(mapperPackage);
            }
            // 如果当前节点为<mapper>
            else {
                // 依次获取resourceurlclass属性
                String resource = child.getStringAttribute("resource");
                String url = child.getStringAttribute("url");
                String mapperClass = child.getStringAttribute("class");
                // 解析resource属性(Mapper.xml文件的路径)
                if (resource != null && url == null && mapperClass == null) {
                    ErrorContext.instance().resource(resource);
                    // Mapper.xml文件解析成输入流
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    // 使用XMLMapperBuilder解析Mapper.xml,并将Mapper Class注册进configuration对象的mapperRegistry容器中
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                    mapperParser.parse();
                }
                // 解析url属性(Mapper.xml文件的路径)
                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();
                }
                // 解析class属性(Mapper Class的全限定名)
                else if (resource == null && url == null && mapperClass != null) {
                    // Mapper Class的权限定名转化成Class对象
                    Class<?> mapperInterface = Resources.classForName(mapperClass);
                    // 注册进configuration对象的mapperRegistry容器中
                    configuration.addMapper(mapperInterface);
                } else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }
}
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    // configuration赋给BaseBuilder
    super(configuration);
    // 创建MapperBuilderAssistant对象
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
}

public void parse() {
    //若当前的Mapper.xml尚未被解析,则开始解析
    // PS:若<mappers>节点下有相同的<mapper>节点,那么就无需再次解析了
    if (!configuration.isResourceLoaded(resource)) {
        // 解析<mapper>节点
        configurationElement(parser.evalNode("/mapper"));
        // 将该Mapper.xml添加至configurationLoadedResource容器中,下回无需再解析
        configuration.addLoadedResource(resource);
        // 将该Mapper.xml对应的Mapper Class注册进configurationmapperRegistry容器中
        bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

加载的资源会在缓存中判断是否已经被解析过,如果没有解析过继续执行,否则跳出。

private void configurationElement(XNode context) {
    try {
        // 获取<mapper>节点上的namespace属性,该属性必须存在,表示当前映射文件对应的Mapper Class是谁
        String namespace = context.getStringAttribute("namespace");
        if (namespace == null || namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
        // namespace属性值赋给builderAssistant
        builderAssistant.setCurrentNamespace(namespace);
        // 解析<cache-ref>节点
        cacheRefElement(context.evalNode("cache-ref"));
        // 解析<cache>节点
        cacheElement(context.evalNode("cache"));
        // 解析<parameterMap>节点
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        // 解析<resultMap>节点
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        // 解析<sql>节点
        sqlElement(context.evalNodes("/mapper/sql"));
        // 解析sql语句
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
}

这里只讲一下resultMapElements、buildStatementFromContext,其他的可以进行查看

resultMapElements

private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    // 获取<ResultMap>上的id属性
    String id = resultMapNode.getStringAttribute("id",
            resultMapNode.getValueBasedIdentifier());
    // 获取<ResultMap>上的type属性(即resultMap的返回值类型)
    String type = resultMapNode.getStringAttribute("type",
            resultMapNode.getStringAttribute("ofType",
                    resultMapNode.getStringAttribute("resultType",
                            resultMapNode.getStringAttribute("javaType"))));
    // 获取extends属性
    String extend = resultMapNode.getStringAttribute("extends");
    // 获取autoMapping属性
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    // resultMap的返回值类型转换成Class对象
    Class<?> typeClass = resolveClass(type);
    Discriminator discriminator = null;
    // resultMappings用于存储<resultMap>下所有的子节点
    List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
    resultMappings.addAll(additionalResultMappings);
    // 获取并遍历<resultMap>下所有的子节点
    List<XNode> resultChildren = resultMapNode.getChildren();
    for (XNode resultChild : resultChildren) {
        // 若当前节点为<constructor>,则将它的子节点们添加到resultMappings中去
        if ("constructor".equals(resultChild.getName())) {
            processConstructorElement(resultChild, typeClass, resultMappings);
        }
        // 若当前节点为<discriminator>,则进行条件判断,并将命中的子节点添加到resultMappings中去
        else if ("discriminator".equals(resultChild.getName())) {
            discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
        }
        // 若当前节点为<result><association><collection>,则将其添加到resultMappings中去
        else {
            // PS:flags仅用于区分当前节点是否是<id><idArg>,因为这两个节点的属性名为name,而其他节点的属性名为property
            List<ResultFlag> flags = new ArrayList<ResultFlag>();
            if ("id".equals(resultChild.getName())) {
                flags.add(ResultFlag.ID);
            }
            resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
        }
    }
    // ResultMapResolver的作用是生成ResultMap对象,并将其加入到Configuration对象的resultMaps容器中(具体过程见下)
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
        return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
        configuration.addIncompleteResultMap(resultMapResolver);
        throw e;
    }
}

buildStatementFromContext

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    //开始处理
    for (XNode context : list) {
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            //解析处理
            statementParser.parseStatementNode();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        }
    }
}
public void parseStatementNode() {
  //获取id <select id="xxxxxxx">
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  //解析<select resultType="com.telecom.Blog" id="selectBlog"> 这里的很多标签
  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultType = context.getStringAttribute("resultType");
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  Class<?> resultTypeClass = resolveClass(resultType);
  String resultSetType = context.getStringAttribute("resultSetType");
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  //获得是哪种操作  select  update  insert  delete
  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  // Include Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);

  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  String resultSets = context.getStringAttribute("resultSets");
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  KeyGenerator keyGenerator;
  //创建key   selectBlog!selectKey
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  //com.telecom.BlogMapper.selectBlog!selectKey
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  //判断是否存在这个key缓存
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    //解析useGeneratedKeys
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }

  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered, 
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
  if (requiredDatabaseId != null) {
    if (!requiredDatabaseId.equals(databaseId)) {
      return false;
    }
  } else {
    if (databaseId != null) {
      return false;
    }
    // skip this statement if there is a previous one with a not null databaseId
    //获取com.telecom.BlogMapper.selectBlog 判断是否已经存在缓存中
    id = builderAssistant.applyCurrentNamespace(id, false);
    if (this.configuration.hasStatement(id, false)) {
      MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
      if (previous.getDatabaseId() != null) {
        return false;
      }
    }
  }
  return true;
}

然后回到XMLMapperBuilder#parse

public void parse() {
    //若当前的Mapper.xml尚未被解析,则开始解析
    // PS:若<mappers>节点下有相同的<mapper>节点,那么就无需再次解析了
    if (!configuration.isResourceLoaded(resource)) {
        // 解析<mapper>节点
        configurationElement(parser.evalNode("/mapper"));
        // 将该Mapper.xml添加至configurationLoadedResource容器中,下回无需再解析
        configuration.addLoadedResource(resource);
        // 将该Mapper.xml对应的Mapper Class注册进configurationmapperRegistry容器中
        bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}
private void bindMapperForNamespace() {
    //获得当前空间 com.telecom.BlogMapper
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
        Class<?> boundType = null;
        try {
            //得到类信息 interface com.telecom.BlogMapper
            boundType = Resources.classForName(namespace);
        } catch (ClassNotFoundException e) {
            //ignore, bound type is not required
        }
        if (boundType != null) {
            //MapperRegistry.hasMapper里面判断是否存在
            if (!configuration.hasMapper(boundType)) {
                //namespace:com.telecom.BlogMapper 放入到加载的资源
                configuration.addLoadedResource("namespace:" + namespace);
                //把类型放入map缓存中
                configuration.addMapper(boundType);
            }
        }
    }
}

在回退到SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)

//代码从这里开始执行
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    //在这里创建xml建造器,并解析xml
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    //解析并构建 并使用DefaultSqlSessionFactoryConfiguration    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.
    }
  }
}

包装一层DefaultSqlSessionFactory

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

这就完成了整个xml的解析,这些都是都在config中。所以configuration这个东西在Mybatis简直无处不在。

上一篇: Mybatis源码分析准备

下一篇: Mybatis源码动态代理调用

猜你喜欢

转载自blog.csdn.net/m0_37444820/article/details/80853906
今日推荐