2023-06-06  阅读(71)
原文作者:惑边 原文地址:https://blog.csdn.net/my_momo_csdn

数据源创建和和策略模式

一、数据源

  • 10-Mybatis源码和设计模式-1(数据源模块和工厂模式,代理模式)中我们了解了数据源模块有三种类型,POOL,UNPOOL和JNDI三种,知道这三种类型的数据源都是通过工厂模式创建出来的,但没有分析数据源的创建过程和创建的策略,仅仅只是静态分析了源码结构。数据源的类型由Environment里的type指定,对于客户端来说不管底层是如何创建数据源的都没有关系,只需要修改配置就能够生产出3种不同类型的数据源。而这种思想恰好和策略模式相符合,因此数据源的创建使用了策略模式。
  • 关于策略模式,可以参考: 03-行为型模式(上)

1.1 配置

  • 配置示例如下:
    <!--配置environment环境 -->
        <environments default="development">
            <!-- 环境配置1,每个SqlSessionFactory对应一个环境 -->
            <environment id="development">
                <transactionManager type="JDBC"/>
                <!-- 配置数据源类型 -->
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc_driver}"/>
                    <property name="url" value="${jdbc_url}"/>
                    <property name="username" value="${jdbc_username}"/>
                    <property name="password" value="${jdbc_password}"/>
                </dataSource>
            </environment>
            <environment id="other">
                <transactionManager type="JDBC"/>
                <!-- 配置数据源类型 -->
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc_driver_other}"/>
                    <property name="url" value="${jdbc_url_other}"/>
                    <property name="username" value="${jdbc_username_other}"/>
                    <property name="password" value="${jdbc_password_other}"/>
                </dataSource>
            </environment>
        </environments>
复制代码

1.2 源码

  • 这小节我们通过跟踪源码来看数据源模块的创建过程,在文章 16-Mybatis 核心流程01-初始化阶段中我分析了Mybatis初始化过程,这个过程中会解析Mybatis的配置文件生产Configuration对象,而数据源类型的配置是在主配置文件的environments节点,由此容易推测数据源的创建应该是在environments节点解析的入口里面。我直接定位到XMLConfigBuilder#parseConfiguration方法,该方法是解析的主流程,而XMLConfigBuilder#environmentsElement就是解析environments节点的入口,也是我们想要找的。

1.2.1 XMLConfigBuilder#environmentsElement

  • XMLConfigBuilder#environmentsElement是解析主配置文件environments节点的方法
    /**
       * 解析environments节点,数据源的初始化在该流程里面完成
       * */
      private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
          //1.根据default获取到多个环境中默认的那一个环境的id
          if (environment == null) {
            environment = context.getStringAttribute("default");
          }
          //2.依次遍历解析所有environment子节点
          for (XNode child : context.getChildren()) {
            //3.获取到id
            String id = child.getStringAttribute("id");
            //4.和default获取到的id一样的才是目标environment,其余的都不需要解析
            if (isSpecifiedEnvironment(id)) {
              //5.解析transactionManager节点
              TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
              //6.解析dataSource节点,得到数据源工厂
              DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
              //7.工厂模式,由数据源工厂得到数据源(这部分可以参考: https://blog.csdn.net/my_momo_csdn/article/details/93489371)
              DataSource dataSource = dsFactory.getDataSource();
              //8.创建Environment,很显然使用了建造者模式
              Environment.Builder environmentBuilder = new Environment.Builder(id)
                  .transactionFactory(txFactory)
                  .dataSource(dataSource);
              //9.将Environment设置到Configuration对象里面去
              configuration.setEnvironment(environmentBuilder.build());
            }
          }
        }
      }
复制代码

1.2.2 XMLConfigBuilder#dataSourceElement

  • XMLConfigBuilder#dataSourceElement是解析主配置文件dataSource子节点的方法。
    /**
       *解析主配置文件dataSource子节点
       * */
      private DataSourceFactory dataSourceElement(XNode context) throws Exception {
        if (context != null) {
            //1.获取数据源类型
          String type = context.getStringAttribute("type");
          //2.获取子节点属性;其实是获取所有的name和value属性,封装到Properties里面,通常这里包含了driver,url,username,password的信息
          Properties props = context.getChildrenAsProperties();
          //3.通过类型获取对应类型的的数据源工厂,这里会到TypeAliasRegistry.TYPE_ALIASES这个Map里面去找type对应的类型
          //通常情况这里面保存的是类的别名和类的Class对象,但是Mybatis把pool,unpool,jndi也保存在里面,对应的类型是对应的工厂类
          //因此获取到之后再newInstance就得到了工厂实例, 最后调用的是TypeAliasRegistry.resolveAlias方法
          DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
          //4.数据源工厂是可以设置属性的,这些属性最后都会设置给数据源DataSource
          factory.setProperties(props);
          return factory;
        }
        //6.没有配置dataSource节点需要抛出异常
        throw new BuilderException("Environment declaration requires a DataSourceFactory.");
      }
      
      
      public <T> Class<T> resolveAlias(String string) {
        try {
          if (string == null) {
            return null;
          }
          // issue #748
          String key = string.toLowerCase(Locale.ENGLISH);
          Class<T> value;
          //通过类别名到TYPE_ALIASES查找对应的类型,得到Class对象
          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);
        }
      }
复制代码
  • 下面是我调试的时候,查看到的TypeAliasRegistry.TYPE_ALIASES这个map里保存的别名和类的对应关系:

202306062340231661.png

  • 到此我们看到了数据源的创建过程 : 获取配置->策略模式实例化工厂->获取数据源->构建Environment;
    详细步骤如下所示。
    ->解析environments节点
    ->获取default表示的id
    ->找到对应id的environment配置
    ->获取transactionManager配置
    ->获取driver,url,username,password配置
    ->根据别名找到对应的工厂类,实例化工厂类->设置属性给工厂类
    ->从工厂获取数据源dataSource
    ->builder模式创建Environment,传入transactionManager和dataSource参数构造Environment对象
    ->将Environment对象设置到Configuration对象
复制代码

二、小结


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文