Mybatis深入之DataSource实例化过程

Mybatis深入之DataSource实例化过程

简介

主要介绍Mybatis启动过程中DataSource实例化的过程、为后面解析一个完整SQL执行过程做个前章。

Mybatis中DataSource体系

MybatisDataSource整体简介

Mybatis中关于数据库的类都在org.apache.ibatis.datasource包中

Mybatis配置文件中关于数据库的配置:

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClassName}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
  • 重点关注<dataSource type="POOLED">的type属性、其有三种取值:

    • POOLED:使用Mybatis自带的数据库连接池来管理数据库连接
    • UNPOOLED:不使用任何数据库连接池来管理数据库连接
    • JNDI:jndi形式使用数据库连接、主要用于项目正常使用的时候

类与类之间的关系:

每一条线都是一种关系、简单解释一下

1. PooledDataSource实现java.sql.DataSource接口

2. PooledDataSource内部持有一个DataSource引用

3. UnpooledDataSource实现java.sql.DataSource接口

4. PooledDataSource内部持有一个UnpooledDataSource引用

5.PooledDataSourceFactory无参构造方法体中将其父类UnpooledDataSourceFactory持有的引用DataSource实例化为PooledDataSource

6. PooledDataSourceFactory继承UnpooledDataSourceFactory

7. UnpooledDataSourceFactory无参构造方法将其持有的引用DataSource实例化为UnpooledDataSource

8. UnpooledDataSourceFactory持有一个DataSource引用、用于返回实例化好的DataSource。

Mybatis中DataSource实例化整体过程

这里以使用Mybatis自带的数据库连接池为例。也就是type为 “POOLED”类型的数据连接。

  1. 根据配置文件中type的类型实例化具体的DataSourceFactory。这里是POOLED所以实例化的是PooledDataSourceFactory。
  2. 通过PooledDataSourceFactory来获取DataSource具体实例:PooledDataSource

对于第一步更详细点的过程:

  • 在Mybatis初始化Configuration对象时、Configuration中属性TypeAliasRegistry同样被实例化、并且在Configuration的无参构造方法中对TypeAliasRegistry注册了许多常用的类(以键值对的形式保存在TypeAliasRegistry的属性private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();中、也包括TypeAliasRegistry无参构造方法注册的基本java类型。
  • 通过配置文件中type指定的”POOLED”在TypeAliasRegistry中查找其对应的类:PooledDataSourceFactory
  • 调用其newInstance()实例化
  • PooledDataSourceFactory继承自UnpooledDataSourceFactory、所以UnpooledDataSourceFactory先被实例化、
  • UnpooledDataSourceFactory无参构造方法中实例化了其DataSource引用为UnpooledDataSource。
  • 接着实例化PooledDataSourceFactory、其无参构造方法体将父类UnpooledDataSourceFactory持有的DataSource实例化为PooledDataSource。
  • PooledDataSource实例化时初始化了一些关于数据库连接池的配置信息
  • PooledDataSource的无参构造方法中将其持有的UnpooledDataSource实例化。
  • UnpooledDataSource中关于数据库连接的属性值在实例化DataSourceFactory之后读取properties值设置到对应属性上。

具体过程

从上一篇中知道Mybatis初始化过程是解析Mybatis配置文件并装配Configuration对象。从Mybatis基础使用中知道Mybatis数据库连接信息的配置是在environments标签中配置的:

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClassName}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

所以想要了解DataSource初始化过程可以从XMLConfigBuilder中的parse方法入手:

  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");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          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());
        }
      }
    }
  }
  • 这里同样有关于数据库事务的配置、具体事务有关的后面再说
  • 主要看如何实例化DataSource、同样从上面代码中我们知道只是将DataSource实例化了而没有进行任何操作、原因是只有具体执行某SQL语句的时候才会使用DataSource来获取数据库连接。

    获取DataSource关键代码:

DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
  • 根据配置文件中dataSource标签中内容实例化DataSourceFactory
  • 通过DataSourceFactory获取DataSource

下面首先看如何根据dataSource标签内容实例化DataSourceFactory

  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      //获取数据库连接池类型: POOLED-使用Mybatis自带数据库连接池。UNPOOL-不使用数据库连接池。这里看POOLED的情况。
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }
  • 上面一段代码关键点在于resolveClass(type)
  • 经过一系列的方法调用、最终返回结果的方法是:TypeAliasRegistry
  @SuppressWarnings("unchecked")
  // throws class cast exception as well if types cannot be assigned
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) return null;
      String key = string.toLowerCase(Locale.ENGLISH); // issue #748
      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);
    }
  }
  • 这里重点在于TypeAliasRegistry是何时实例化的

    • 上一篇初始化过程中知道Configuration中有一个私有变量protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    • 进一步看看Configuration实例化的时候其无参构造函数体就知道其缘由
  public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }
  • 从上面可以看出TypeAliasRegistry在实例化之后并初始化了其内部私有变量private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();来保存一些Type alias(类型别名)供后面程序使用。

从上面代码typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);可以看出最后返回的是PooledDataSourceFactory

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

}
  • PooledDataSourceFactory继承UnpooledDataSourceFactory

其中UnpooledDataSourceFactory拥有一个DataSource的protected级别的属性protected DataSource dataSource;并且其构造函数:

  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }

对比PooledDataSourceFactory的构造函数:

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

知道最终UnpooledDataSourceFactory的protected DataSource dataSource;实例是:PooledDataSource

到这里只要知道PooledDataSource是什么、那么返回的DataSource就是什么。

当然在这之前还需要一步、就是将配置文件中的数据库连接信息设置到最后生成的DataSourceFactory(在这里就是PooledDataSourceFactory)中去。这个过程中使用了一个Mybatis很长用的用于操作反射的封装类:MetaObject。提供了一些简便的获取、设置类等通过反射来操作类的方法、以后有时间专门看一眼。

下面的主要目标就是看PooledDataSource调用其无参构造方法时到底做了什么。

PooledDataSource是java.sql.DataSource的一个实现类、其属性与无参构造方法如下:


  private static final Log log = LogFactory.getLog(PooledDataSource.class);

  private final PoolState state = new PoolState(this);

  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS
  protected int poolMaximumActiveConnections = 10;
  protected int poolMaximumIdleConnections = 5;
  protected int poolMaximumCheckoutTime = 20000;
  protected int poolTimeToWait = 20000;
  protected String poolPingQuery = "NO PING QUERY SET";
  protected boolean poolPingEnabled = false;
  protected int poolPingConnectionsNotUsedFor = 0;

  private int expectedConnectionTypeCode;

  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }
  • 设置了一些作为数据库连接池初始化使用的参数
  • 无参方法体中实例话了属性dataSource = new UnpooledDataSource();
  • UnpooledDataSource无参构造函数是空方法题
  • 要注意的是:前面实例化DataSourceFactory的时候最后一步是设置属性。其实就是通过MetaObject来将属性值设置到UnpooledDataSource的数据库连接属性上了。

到这里、关于数据库DataSource类的实例话也就结束了。

补充

对数据库实例化做个总结:

当使用数据库连接池时、即<dataSource type="POOLED">时、DataSourceFactory具体实例是PooledDataSourceFactory。返回的DataSource具体实例是内部持有UnpooledDataSource实例的PooledDataSource。

当不使用数据库连接池时、即<dataSource type="UNPOOLED"> 时、DataSourceFactory具体实例是UnpooledDataSourceFactory。返回的DataSource具体实例是UnpooledDataSource实例。

更多内容:Mybatis 目录

时间: 2024-12-26 21:20:56

Mybatis深入之DataSource实例化过程的相关文章

springmvc学习总结(二) -- maven+springmvc+spring+mybatis+mysql详细搭建整合过程讲解

@[email protected] 写在最前 之前分享过下面这几篇: mybatis学习笔记(五) -- maven+spring+mybatis从零开始搭建整合详细过程(上)(附demo和搭建过程遇到的问题解决方法) mybatis学习笔记(六) -- maven+spring+mybatis从零开始搭建整合详细过程(下) springmvc学习笔记(一) -- 从零搭建,基础入门 这一篇,在这些练习的基础上,将它们整合在一起! 搭建步骤如下 一.新建maven项目,配置环境,测试是否配置成

mybatis学习笔记(六) -- maven+spring+mybatis从零开始搭建整合详细过程(下)

继续 mybatis学习笔记(五) -- maven+spring+mybatis从零开始搭建整合详细过程(上) 五.使用监听器启动Spring容器 1.修改pom.xml文件,添加Spring-web 2.修改web.xml,配置启动Spring容器 3.新建BookServer 4.新建BookServlet 5.修改ApplicationContext.xml 6.测试 继续!!! 五.使用监听器启动Spring容器 1.修改pom.xml,添加Spring-web包(注:上一篇中的pom

深夜睡不着,第二篇随笔,说说js的创建实例化过程

媳妇白天加班太累了,我呢,白天睡太多了,晚上太过于亢奋,自己一个人偷偷的拿着笔记本到客厅写博客~ 上一篇可能很多人看到了觉得就是个joke,那个真的是一个joke,但是在实际开发过程中,很多年轻的coder对于写不写分号很不以为然,要知道,真实生产环境下的代码要远比我栗子中给的代码要复杂得多,因此很有可能不用我的误导,你就看不出来,因此浪费了一下午的宝贵开发时间,所以写代码还是要规范一些. 第二篇文章我依旧不想讲太过于深入的技术,还是说两个“花边新闻”,聊以自慰罢了,看官有兴致你就看,没兴致也可

java4android (继承中的子类实例化过程)

生成子类的过程 见代码: class Person { String name; int age; Person(){ System.out.print("Person的无参数构造函数"); } Person(String name,int age){ this.name = name; this.age = age; System.out.print("Person的有2个参数的构造函数"); } void eat(){ System.out.print(&quo

【Spring源码分析】非懒加载的Bean实例化过程(下篇)

doCreateBean方法 上文[Spring源码分析]非懒加载的Bean实例化过程(上篇),分析了单例的Bean初始化流程,并跟踪代码进入了主流程,看到了Bean是如何被实例化出来的.先贴一下AbstractAutowireCapableBeanFactory的doCreateBean方法代码: 1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[]

面向对象(子父类中构造函数的特点-子类实例化过程)

/* 3,子父类中的构造函数. 在对子类对象进行初始化时,父类的构造函数也会运行, 那是因为子类的构造函数默认第一行有一条隐式的语句 super(); super():会访问父类中空参数的构造函数.而且子类中所有的构造函数默认第一行都是super(); 为什么子类一定要访问父类中的构造函数. 因为父类中的数据子类可以直接获取.所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的.//指在在父类构造函数初始化 所以子类在对象初始化时,要先访问一下父类中的构造函数. 如果要访问父类中指定

一个对象的实例化过程【重点】

一.过程  Person p = new Person();  1,JVM会去读取指定路径下的Person.class文件,并加载进内存,    并会先加载Person的父类(如果有直接父类的情况下)  2,在堆内存中开辟空间,分配地址.  3,并在对象空间中,对对象中的属性进行默认初始化  4,调用对应的构造函数,进行初始化  5,在构造函数中,第一行会先调用父类中的构造函数进行初始化.  6,父类初始化完毕后,再对子类的属性,进行显示初始化.  7,指定构造函数的特定初始化  8,初始化完毕

python基础8之类的实例化过程剖析

一.概述 之前我们说关于python中的类,都一脸懵逼,都想说,类这么牛逼到底是什么,什么才是类?下面我们就来讲讲,什么是类?它具有哪些特性. 二.类的语法 2.1 语法 class dog(object): #用class定义类 "dog class" #对类的说明 def __init__(self,name): #构造函数或者是构造方法,也可以叫初始化方法 self.name = name def sayhi(self): #类方法 "sayhi funcation&q

【python】-- 类的实例化过程、特征、共有属性和私有属性

实例化过程 1.类的定义和语法 class dog(object): #用class定义类 "dog class" #对类的说明 def __init__(self,name): #构造函数或者是构造方法,也可以叫初始化方法 self.name = name def sayhi(self): #类方法 "sayhi funcation" #对类方法的说明 print("hello,i am a dog,my name is ",self.name