myBatis源码解析

// 对外提供的数据源工厂接口

public interface DataSourceFactory {

  // 设置配置信息

  void setProperties(Properties props);

// 获取数据源

  DataSource getDataSource();

}

复制代码

复制代码

// 非池化的数据源工厂类

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(); // 构造一个非池化的数据源(下文分析数据源详细代码)

  }

  public void setProperties(Properties properties) { // 对数据源进行配置,此处设计反射包的知识(本章重点不在这,可忽略)

    Properties driverProperties = new Properties();

    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource); // 将dataSource类转为metaObject类

    for (Object key : properties.keySet()) {

      String propertyName = (String) key;

      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) { // 若是数据库驱动配置

        String value = properties.getProperty(propertyName);

        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value); // driverProperties存储数据库驱动参数

      } else if (metaDataSource.hasSetter(propertyName)) { // 如果有set方法

        String value = (String) properties.get(propertyName);

        // 根据属性类型进行类型的转换,主要是 Integer, Long, Boolean 三种类型的转换

        Object convertedValue = convertValue(metaDataSource, propertyName, value);

        // 设置DataSource 的相关属性值

        metaDataSource.setValue(propertyName, convertedValue);

      } else {

        throw new DataSourceException("Unknown DataSource property: " + propertyName);

      }

    }

    // 设置 DataSource.driverProerties 属性值

    if (driverProperties.size() > 0) {

      metaDataSource.setValue("driverProperties", driverProperties);

    }

  }

  // 获取数据源

  public DataSource getDataSource() {

    return dataSource;

  }

// 对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;

  }

}

复制代码

复制代码

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

  public PooledDataSourceFactory() {

    // dataSource实现类变为PooledDataSource

    this.dataSource = new PooledDataSource();

  }

}

复制代码

unPooledDataSourceFactory主要工作是对数据源进行参数配置,并提供获取数据源方法。分析PooledDataSourceFactory源码,只是继承unPooledDataSourceFactory,将DataSource实现类改变为PooledDataSource。

unPooledDataSource源码分析:基本的数据源实现都实现了DataSource接口,重写获取数据库连接的方法。unPooledDataSource从类名可知,不支持数据库连接的池化。也就是说,每来一个获取连接请求,就新建一个数据库连接。让我们看源码验证下。

复制代码

public class UnpooledDataSource implements DataSource {


  private ClassLoader driverClassLoader; // 数据库驱动类加载器

  private Properties driverProperties; // 有关数据库驱动的参数

  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>(); // 缓存已注册过的数据库驱动

  private String driver; // 数据库驱动

  private String url; // 数据库名

  private String username; // 连接用户名

  private String password; // 密码

  private Boolean autoCommit; // 是否自动提交

  private Integer defaultTransactionIsolationLevel; // 事物隔离级别

  static { // 初始化

    Enumeration<Driver> drivers = DriverManager.getDrivers();  // DriverManager中已存在的数据库驱动加载到数据库驱动缓存

    while (drivers.hasMoreElements()) {

      Driver driver = drivers.nextElement();

      registeredDrivers.put(driver.getClass().getName(), driver);

    }

  }

  .....



  public Connection getConnection() throws SQLException {

    return doGetConnection(username, password);

  }


  // 获取数据库连接

  private Connection doGetConnection(Properties properties) throws SQLException {

    initializeDriver(); // 初始化数据库驱动

    Connection connection = DriverManager.getConnection(url, properties); // 此处每次获取连接,就新建一个数据库连接

    configureConnection(connection); // 设置数据库是否自动提交,设置数据库事物隔离级别

    return connection;

  }

  private synchronized void initializeDriver() throws SQLException {

    // 若此驱动还没初始化,则进行初始化

    if (!registeredDrivers.containsKey(driver)) {

      Class<?> driverType;

      try {

        if (driverClassLoader != null) {

          driverType = Class.forName(driver, true, driverClassLoader);

        } else {

          driverType = Resources.classForName(driver);

        }

        // DriverManager requires the driver to be loaded via the system ClassLoader.

        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html

        Driver driverInstance = (Driver)driverType.newInstance();

        DriverManager.registerDriver(new DriverProxy(driverInstance));

        registeredDrivers.put(driver, driverInstance);

      } catch (Exception e) {

        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);

      }

    }

  }

  private void configureConnection(Connection conn) throws SQLException {

    if (autoCommit != null && autoCommit != conn.getAutoCommit()) {

      conn.setAutoCommit(autoCommit);

    }

    if (defaultTransactionIsolationLevel != null) {

      conn.setTransactionIsolation(defaultTransactionIsolationLevel);

    }

  }

  ....

}

PoolConnection是一个connection代理类,里面封装了真实的连接与代理连接,现在我们先来分析PoolConnection的源码。

复制代码

class PooledConnection implements InvocationHandler {  // 连接代理类

  private static final String CLOSE = "close";

  private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };

  private int hashCode = 0;

  private PooledDataSource dataSource; // 数据源

  private Connection realConnection; // 被代理的真实连接

  private Connection proxyConnection; // 代理连接

  private long checkoutTimestamp; // 从连接池中取出连接的时间

  private long createdTimestamp; // 连接建立的时间

  private long lastUsedTimestamp; // 连接上次使用的时间

  private int connectionTypeCode; // 用于标注该连接所在的连接池

  private boolean valid; // 连接有效的标志

复制代码

PooledConnection实现了InvocationHandler接口,则可见是一个代理对象。查看属性可知,内部有真实连接与代理连接,并附带连接的一些记录信息。查看该类的构造方法。

复制代码

public PooledConnection(Connection connection, PooledDataSource dataSource) {

    this.hashCode = connection.hashCode();

    this.realConnection = connection;

    this.dataSource = dataSource;

    this.createdTimestamp = System.currentTimeMillis();

    this.lastUsedTimestamp = System.currentTimeMillis();

    this.valid = true; // 该链接是否有效

    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);  // 使用动态代理生成连接的代理类

  }

  /*

  * Invalidates the connection

  */

  // 将该链接置为无效   

  public void invalidate() {

    valid = false;

  }

复制代码

查看构造方法可知,内部除了初始化一些属性外,还将连接的代理类也进行初始化了。那代理类究竟做了什么,查看重写的invoke方法源码。

复制代码

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  // 代理方法

    String methodName = method.getName(); // 获取方法名

    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { // 若是close方法,则将该连接放入数据源中

      dataSource.pushConnection(this);

      return null;

    } else {

      try {

        if (!Object.class.equals(method.getDeclaringClass())) {  // 若要执行的方法不是object方法,则检查连接的有效性

          // issue #579 toString() should never fail

          // throw an SQLException instead of a Runtime

          checkConnection();

        }

        return method.invoke(realConnection, args);  //执行真实的方法

      } catch (Throwable t) {

        throw ExceptionUtil.unwrapThrowable(t);

      }

    }

  }

  private void checkConnection() throws SQLException {

    if (!valid) {

      throw new SQLException("Error accessing PooledConnection. Connection is invalid.");

    }

  }

复制代码

由源码可知,代理连接在执行方法时,会先检查此连接的有效性,然后执行真实的方法。分析完PoolConnection后,对PoolState进行源码解析。

复制代码

public class PoolState {  // 连接池状态信息

  protected PooledDataSource dataSource; // 此状态信息关联的数据源

  protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>(); // 空闲连接列表

  protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>(); // 活跃连接列表

  protected long requestCount = 0; // 请求数

  protected long accumulatedRequestTime = 0; // 累加请求所用时间

  protected long accumulatedCheckoutTime = 0; // 累加占用连接所用时间

  protected long claimedOverdueConnectionCount = 0;  // 连接超时的数量

  protected long accumulatedCheckoutTimeOfOverdueConnections = 0; // 累加超时的连接超时的时间

  protected long accumulatedWaitTime = 0; // 累加等待获取连接所用时间

  protected long hadToWaitCount = 0; // 等待获取连接的线程数

  protected long badConnectionCount = 0; // 失效的连接数

复制代码

PoolState是对DataSource的状态管理类,主要包括如累计连接超时时间,失效连接的获取等一些状态信息的管理。除了包括一些数据库连接的记录信息外,内部还维护了两个数据库连接的列表idleConnections,activeConnections.。分别用来存放空闲的数据库连接列表,活跃的数据库连接列表,针对此两个列表的操作,下文在分析PooledDataSource时会进行详细介绍。

对PoolConnection和PoolState分析结束后,具体分析PoolDataSource源码。

复制代码

public class PooledDataSource implements DataSource {

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

  private final PoolState state = new PoolState(this); // 维护数据源的状态

  private final UnpooledDataSource dataSource; // 使用UnpooledDataSource来建立真正的连接

  // OPTIONAL CONFIGURATION FIELDS

  protected int poolMaximumActiveConnections = 10; // 最大活跃的连接数

  protected int poolMaximumIdleConnections = 5;  // 最大空闲的连接数

  protected int poolMaximumCheckoutTime = 20000; // 最大checkout时间(checkOutTime指的是从数据源中获取连接到归还连接的时间)

  protected int poolTimeToWait = 20000; // 最大等待时间

  protected String poolPingQuery = "NO PING QUERY SET"; // 使用该语句来验证该连接是否有效

  protected boolean poolPingEnabled = false;

  protected int poolPingConnectionsNotUsedFor = 0;

  private int expectedConnectionTypeCode; // hashcode

深圳网站建设www.sz886.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352