MyBatis源码解析之基础模块—DataSource

本文深入解析MyBatis的DataSource模块,包括DataSource的作用、JDK中的DataSource接口及其意义,以及MyBatis中DataSourceFactory的三种实现:UnpooledDataSourceFactory、PooledDataSourceFactory和JndiDataSourceFactory。此外,文章还详细介绍了非池化数据源UnpooledDataSource和池化数据源PooledDataSource的实现原理,包括PooledConnection和PoolState组件。
摘要由CSDN通过智能技术生成

在这里插入图片描述

MyBatis源码解析之基础模块—DataSource

前文回顾

上一章节我们一起学习了Mapper接口绑定的源码逻辑。本次我们学习MyBatis的DataSource数据源模块。

为解决这种操作方式的弊端,在mybatis版本中提供了binding模块。从而能够在编译期就能够发现问题。同时通过采用jdk动态代理模式,开发者只需要要编写对应的接口即可完成持久层的开发工作。即降低工作量,有大大降低出错概率。

接下来,我们将通过源码详细介绍binding的执行逻辑。

背景知识

因为常见的数据源都会基于javax.sql.Datasource实现。Mybatis的数据源实现也是基于实现javax.sql.Datasource来设计的,也是在介绍MyBatis数据源实现之前,咱们先了解下JDK的DataSource。

关于jdk中对DataSource在Oracle官网DataSource介绍有如下一段描述:

A factory for connections to the physical data source that this DataSource object represents. An alternative to the DriverManager facility, a DataSource object is the preferred means of getting a connection. An object that implements the DataSource interface will typically be registered with a naming service based on the Java™ Naming and Directory (JNDI) API.

The DataSource interface is implemented by a driver vendor. There are three types of implementations:

  1. Basic implementation – produces a standard Connection object
  2. Connection pooling implementation – produces a Connection object that will automatically participate in connection pooling. This implementation works with a middle-tier connection pooling manager.
  3. Distributed transaction implementation – produces a Connection object that may be used for distributed transactions and almost always participates in connection pooling. This implementation works with a middle-tier transaction manager and almost always with a connection pooling manager.

A DataSource object has properties that can be modified when necessary. For example, if the data source is moved to a different server, the property for the server can be changed. The benefit is that because the data source’s properties can be changed, any code accessing that data source does not need to be changed.

A driver that is accessed via a DataSource object does not register itself with the DriverManager. Rather, a DataSource object is retrieved though a lookup operation and then used to create a Connection object. With a basic implementation, the connection obtained through a DataSource object is identical to a connection obtained through the DriverManager facility.

An implementation of DataSource must include a public no-arg constructor.

翻译过来就是:

一个用于连接到此DataSource对象表示的物理数据源的工厂。作为DriverManager工具的替代方法,DataSource对象是获取连接的首选方法。通常将基于Java™命名和目录(JNDI)API向实现命名服务的对象注册实现DataSource接口的对象。

DataSource接口由驱动程序供应商实现。共有三种类型的实现:

基本实现-产生一个标准的Connection对象
连接池实现-产生一个Connection对象,该对象将自动参与连接池。此实现与中间层连接池管理器一起使用。
分布式事务实现-产生一个Connection对象,该对象可用于分布式事务,并且几乎总是参与连接池。此实现与中间层事务管理器一起使用,并且几乎总是与连接池管理器一起使用。
DataSource对象具有可以在必要时修改的属性。例如,如果将数据源移动到其他服务器,则可以更改服务器的属性。好处在于,因为可以更改数据源的属性,所以不需要更改访问该数据源的任何代码。

通过DataSource对象访问的驱动程序不会在DriverManager中注册自身。而是通过查找操作检索DataSource对象,然后将其用于创建Connection对象。通过基本实现,通过DataSource对象获得的连接与通过DriverManager工具获得的连接相同。

DataSource的实现必须包括一个公共的无参数构造函数。

根据 DataSource的实现必须包括一个公共的无参数构造函数的描述。

这也是下面分析源码时看到的为什么池化数据源PoolDataSource与非池化数据源UnpooledDataSource都有显性定义无参构造函数的原因。

关于DataSource就简单介绍到这里,有兴趣的同学可以查阅相关资料及jdk源码等。

架构设计

DataSource模块所在包路径为org.apache.ibatis.datasource,其具体划分如下:

datasource
- jndi
  - JndiDataSourceFactory
- pooled
  - PooledConnection
  - PooledDataSource
  - PooledDataSourceFactory
  - PoolState
- unpooled
  - UnpooledDataSource
  - UnpooledDataSourceFactory
- DataSourceException
- DataSourceFactory

对应的类架构设计图如下:

在这里插入图片描述

从架构图中,我们很显然发现,该架构采用经典的工厂方法设计模式(关于设计模式的介绍各位可以参阅本人的另一个设计模式专题,或者其他资料)

源码解读

DataSourceFactory

DataSourceFactory接口只提供两个方法:setProperties()设置数据源相关属性;getDataSource()获取数据源。

/**
 * 数据源工厂接口类,只提供两个方法:
 * 1.设置相关配置属性
 * 2.获取数据源
 * 3.该接口有三个实现类:PooledDataSourceFactory,UnpooledDataSourceFactory,JndiDataSourceFactory
 */
public interface DataSourceFactory {
   

  //设置属性(其目的是位datasource填充配置属性)
  void setProperties(Properties props);

  //获取数据源对象
  DataSource getDataSource();

}

MyBatis提供三种DataSourceFactory的实现方式:JndiDataSourceFactoryUnpooledDataSourceFactoryPooledDataSourceFactory。现对其逐一介绍。

UnpooledDataSourceFactory

DataSourceFactoryUnpooledDataSourceFactory的实现,首先会在其构造方法中直接实例化非池化的数据源UnpooledDataSource ,并 通过getDataSource()方法获取该数据源。UnpooledDataSource数据源中相关属性的填充则通过setProperties()进行设置。具体细节及说明,请参阅如下源码:

package org.apache.ibatis.datasource.unpooled;

import java.util.Properties;
import javax.sql.DataSource;

import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

public class UnpooledDataSourceFactory implements DataSourceFactory {
   

  private static final String DRIVER_PROPERTY_PREFIX = "driver.";
  private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();

  protected DataSource dataSource;

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

  /**
   * 从properties中获取对应的配置信息
   */
  @Override
  public void setProperties(Properties properties) {
   
    Properties driverProperties = new Properties();
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    for (Object key : properties.keySet()) {
   
      String propertyName = (String) key;
      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
   
        //"driver."开头的为DataSource相关配置,均保存到driverProperties对象中,并最终会设置在metaDataSource中
        String value = properties.getProperty(propertyName);
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      } else if (metaDataSource.hasSetter(propertyName)) {
   
        //普通配置属性直接设置在metaDataSource中
        String value = (String) properties.get(propertyName);
        //调用私有类型转换方法
        Object convertedValue = convertValue(metaDataSource, propertyName, value);
        metaDataSource.setValue(propertyName, convertedValue);
      } else {
   
        throw new DataSourceException("Unknown DataSource property: " + propertyName);
      }
    }
    /** driverProperties对象有值的情况下才设置 */
    if (driverProperties.size() > 0) {
   
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }

  @Override
  public DataSource getDataSource() {
   
    return dataSource;
  }

  /** 根据propertyName的属性类型转换对应的value值,主要处理Integer,long及boolean三种 */
  private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
   
    Object convertedValue = value;
    Class<?> targetType = metaDataSource.getSetterType(propertyName);
    if (targetType == Integer.class || targetType == int.class) {
   
      convertedValue = Integer.valueOf(value);
    } else if (targetType == Long.class || targetType == long.class) {
   
      convertedValue = Long.valueOf(value);
    } else if (targetType == Boolean.class || targetType == boolean.class) {
   
      convertedValue = Boolean.valueOf(value);
    }
    return convertedValue;
  }

}
PooledDataSourceFactory

PooledDataSourceFactory实现则更为简单,他并没有复写setProperties()getDataSource()方法,而是直接继承UnpooledDataSourceFactory,唯一区别就是构造方法中dataSource实例化对象为PooledDataSource,具体代码如下

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
   

  public PooledDataSourceFactory() {
   
    //与UnpooledDataSourceFactory唯一的区别
    this.dataSource = new PooledDataSource();
  }
}
JndiDataSourceFactory

介绍JndiDataSourceFactory之前,先简单介绍下JNDI(全称为Java Naming and Directory Interface), JNDI是 SUN 公司提供的一种标准的 Java 命名系统接口,JNDI 提供统一的客户端 API,通过不同的访问提供者接口 JNDI 服务供应接口 ( SPI ) 的实现,由管理者将 JNDI API 映射为特定的命名服务和目录系统,使得 Java 应用程序可以和这些命名服务和目录服务之间进行交互。其根本目的还是降低耦合性,提供部署的灵活性,降低维护成本。

JndiDataSourceFactory实现与上述两种有所不同,从其命名的方式就可以知道DataSource的设置需要依赖具体的数据源厂商,因此不能在构造函数中进行实例化。所以只能从配置中读取相关信息,然后根据Context上下文环境,通过lookup的方式进行实例化。

具体细节及说明,请看如下代码及相关注释:

package org.apache.ibatis.datasource.jndi;

import java.util.Map.Entry;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;

/**
 * Jndi数据源工厂类
 */
public class JndiDataSourceFactory implements DataSourceFactory {
   

  public static final String INITIAL_CONTEXT = "initial_context";
  public static final String DATA_SOURCE = "data_source";
  public static 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值