mybatis源码解读(6)

8 篇文章 0 订阅
7 篇文章 0 订阅

配置文件的解析

前面几节简略讲了怎么一步步组装sql语句,一步步往jdbc写法靠近。其中,也留了很多问题。比如
SqlSessionFactory和sqlSession是怎么构建的?
configuration中拿出包含sql的statement是什么时候初始化的?
一些hander为什么是默认的?如果不要默认在哪里配置?
等等。

为了上述问题,我们来看看这么东西是怎么来的。
在这之前,需要一些预备知识。
XPATH。
推荐了解文章:http://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true); // never forget this!
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new InputSource(inputStream));
        XPathFactory xPathFactory = XPathFactory.newInstance();

        XPath xpath = xPathFactory.newXPath();
        Object config = xpath.evaluate("/configuration",doc, XPathConstants.NODE);
        Node node = (Node) config;
        System.out.println(node);

以上代码片段不是源码,是我写的xpath方式解析xml的小例子。
大致步骤分为:
第一环境:建立文件流InputStream–>新建DocumentBuilderFactory–>拿到DocumentBuilder–>再拿到Document 对象
第二环境:创建XPathFactory–>拿到XPath.
主要实现:通过上述两个前提执行查找方法。
Object config = xpath.evaluate(“/configuration”,doc, XPathConstants.NODE);
Node node = (Node) config;
这样就能查新到以configuration节点。
这个xpath解析方法就是mybatis使用的解析方法。
下面贴上相应的主配置文件。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/erik?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="yushu"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/erik/PersonMapper.xml"/>
    </mappers>
</configuration>

我们回过头来看看SqlSessionFactory和SqlSession的创建。

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建SqlSession
SqlSession session = sqlSessionFactory.openSession();

前面两行是创建文件流,和例子中的是一样的,关键在第三和第四两行。
在查看这两行代码前,先给的整体概念:
SqlSessionFactoryBuilder:SqlSessionFactory的创建类,里面包含了各种入参的build方法。
XMLConfigBuilder:这个可不是创建XMLConfig类的(也没这个类),这个类的作用是里面有一大堆parse方法。把xml里面的配置信息写到相对应的配置对象中。
XPathParser:对XPath的进一步封装,主要是里面的evaluate等方法查找xml中相关的节点。
Configuration:mybatis中大而全的配置对象,里面包含了几乎所以的配置信息。

读取配置文件的工作模式是XPathParser解析xml,返回的东西交给XMLConfigBuilder放到Configuration中,然后用Configuration创建SqlSessionFactoryBuilder.

现在看下build方法。

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream)
public SqlSessionFactory build(InputStream inputStream) {
   return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      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.
      }
    }
  }

首先通过构造方法使用输入流,环境,以及properties文件来创建XMLConfigBuilder对象。再调用parse方法解析配置文件内容,返回Configration对象中,再使用SqlSessionFactory的build方法创建SqlSessionFactory对象。
这里的主要方法为parse方法。

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

parsed对象控制了每一个XMLConfigBuilder只执行一次parse方法。
parseConfiguration方法传入了跟节点(/configuration的写法意思为从根节点开始选取configuration,具体请参见xpath的写法)

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"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

我们可以看到,这里已经开始解析各个节点的内容了。
至于各个节点什么意思,可以到mybatis官方说明文档中看看(http://mybatis.github.io/mybatis-3/zh/configuration.html
对照着相应的配置查看。
我们先看下第一个解析
propertiesElement(root.evalNode(“properties”));

root.evalNode(“properties”),这里的root指的是上文中得到的configuration,这句的意思是在configuration下查找properties节点。
properties属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递,更多请参考官方文档。

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

properties在配置文件中的形式是这样的。
再来看看解析的代码:

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      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.");
      }
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

这里的context就是上文中查找的properties节点了。
我们看下逻辑:
拿到properties的子节点,存放在defaults中。
再获取properties节点的resource和url属性,先取resource中的属性值,再取url中的属性值,再获取configuration中的变量,然后都放到defaults去,最后更新configuration的Variables。
这些代码,我们能得到一些信息:
1.properties支持在资源文件中配置,也支持在url中配置,两个一起配置,只会资源文件生效。
2.属性值的优先关系。最后放置的最高,因为不会被其他覆盖,我们可以看到,configuration.getVariables();是最后放的,这个的第一次初始化是在创建XMLConfigBuilder时,传入了一个Propertis对象创建的,也就是说创建SqlSessionFactory创建时,传入的属性值优先级最高。
其次是resourse或者url的属性,最低的优先级就是这个配置文件配置的字节点属性值。

这里有这么多的解析。
我想说其他的差不多。就略过了,其实不然,每个都有自己的特色,我争取每个的分析都写出来。同时也是加深对mybatis理解的过程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis是一个开源的持久层框架,它的配置文件包含了会深深影响MyBatis行为的设置和属性信息。配置文件的顶层结构包括properties(属性)、settings(设置)、typeAliases(类型别名)、typeHandlers(类型处理器)、objectFactory(对象工厂)、plugins(插件)、environments(环境配置)、mappers(映射器)等。\[3\] 在MyBatis源码,有一个方法build,该方法用于解析动态脚本并生成SqlSource对象。在该方法,首先会调用parseDynamicTags方法解析动态标签,得到一个包含多个SqlNode的列表contents。然后,通过将contents传入MixedSqlNode的构造函数,创建一个MixedSqlNode对象rootSqlNode。接下来,根据是否为动态脚本,分别创建DynamicSqlSource或RawSqlSource对象,并将configuration、rootSqlNode和parameterType作为参数传入构造函数。最后,返回创建的SqlSource对象。\[2\] 另外,还有一个方法configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql),该方法用于创建StatementHandler对象。具体的解读需要查看该方法的实现代码。 #### 引用[.reference_title] - *1* [mybatis源码深度解析](https://blog.csdn.net/qq_31359923/article/details/126582701)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] - *2* [【MybatisMybatis源码解读](https://blog.csdn.net/keepfriend/article/details/124356649)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] - *3* [mybatis源码解析](https://blog.csdn.net/weixin_43189971/article/details/125418419)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值