最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis基礎(chǔ)支持DataSource實(shí)現(xiàn)源碼解析

 更新時(shí)間:2023年02月05日 11:11:28   作者:念念清晰  
這篇文章主要為大家介紹了MyBatis基礎(chǔ)支持DataSource實(shí)現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

DataSource

在數(shù)據(jù)庫(kù)應(yīng)用中,客戶(hù)端與數(shù)據(jù)庫(kù)服務(wù)端建立的連接對(duì)象(Connection)是寶貴的資源,每次請(qǐng)求數(shù)據(jù)庫(kù)都創(chuàng)建連接,使用完畢后會(huì)銷(xiāo)毀連接,這是一種很浪費(fèi)資源的操作。因此Java提出了DataSource接口。可以把它當(dāng)作一個(gè)連接池。程序初始化時(shí),創(chuàng)建一批連接放入到連接池中,如果需要請(qǐng)求數(shù)據(jù)庫(kù)就從連接池中取出連接對(duì)象(Connection)使用完畢后把連接歸還給連接池。這樣就減少了每次請(qǐng)求都創(chuàng)建、銷(xiāo)毀連接的步驟,從而提高數(shù)據(jù)庫(kù)性能。

package javax.sql;
public interface DataSource  extends CommonDataSource, Wrapper {
  // 最重要的方法
  Connection getConnection() throws SQLException;
  // 其他方法不再列出
}

Java只是在JDK1.4版本發(fā)布了該接口規(guī)范。具體實(shí)現(xiàn)需要用戶(hù)自己實(shí)現(xiàn)。MyBatis中提供了3種DataSource接口的實(shí)現(xiàn)。

  • UnpooledDataSource
  • PooledDataSource
  • JNDI方式的接口(不在本文討論范圍)

下面著重分析1和2這兩種DataSource的實(shí)現(xiàn)。

UnpooledDataSource

UnpooledDataSource顧名思義,他是非池化的DataSource,說(shuō)白了和普通的Connection沒(méi)什么區(qū)別。通過(guò)UnpooledDataSource過(guò)去連接每次都需要重新創(chuàng)建一個(gè)Connection。我們來(lái)看下它的getConnection實(shí)現(xiàn)方法。

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;
}

在UnpooledDataSource#getConnection方法中,調(diào)用了doGetConnection方法,參數(shù)是username和password,該方法也就是通過(guò)用戶(hù)名和密碼獲取數(shù)據(jù)庫(kù)連接的意思。doGetConnection具體實(shí)現(xiàn)就使用了DriverManager來(lái)獲取連接對(duì)象。這是JDBC原生獲取連接對(duì)象的方式。

值得一說(shuō)的是:UnpooledDataSource的其他方法都是基于DriverManager實(shí)現(xiàn)的。也就是說(shuō),使用UnpooledDataSource作為連接池的話(huà)等價(jià)于沒(méi)有使用連接池。

PooledDataSource

PooledDataSource才是真正意義上的連接池,它提供了連接池的大小(默認(rèn)10)、最大活躍連接數(shù)量、空閑連接數(shù)量等蠶食設(shè)置。并且對(duì)Connection對(duì)象進(jìn)行了JDK動(dòng)態(tài)代理,重寫(xiě)了Connection的close方法。使得Connection對(duì)象在調(diào)用close方法是不是真正的關(guān)閉連接,而是把自定義關(guān)閉行為,MyBatis的關(guān)閉邏輯就是把Connection對(duì)象歸還連接池。

我們先看下PooledDataSource的幾個(gè)重要字段信息

public class PooledDataSource implements DataSource {
  // PooledDataSource真正管理連接狀態(tài)的是PoolState,后面會(huì)詳細(xì)說(shuō)明
  private final PoolState state = new PoolState(this);
  // UnpooledDataSource上面說(shuō)過(guò)和普通的Connection無(wú)異
  private final UnpooledDataSource dataSource;
  //正在使用連接的數(shù)量
  protected int poolMaximumActiveConnections = 10;
  //空閑連接數(shù)
  protected int poolMaximumIdleConnections = 5;
  //在被強(qiáng)制返回之前,池中連接被檢查的時(shí)間
  protected int poolMaximumCheckoutTime = 20000;
  //這是給連接池一個(gè)打印日志狀態(tài)機(jī)會(huì)的低層次設(shè)置,還有重新 嘗試獲得連接, 這些情況下往往需要很長(zhǎng)時(shí)間 為了避免連接池沒(méi)有配置時(shí)靜默失 敗)。
  protected int poolTimeToWait = 20000;
  //發(fā)送到數(shù)據(jù)的偵測(cè)查詢(xún),用來(lái)驗(yàn)證連接是否正常工作,并且準(zhǔn)備 接受請(qǐng)求。默認(rèn)是“NO PING QUERY SET” ,這會(huì)引起許多數(shù)據(jù)庫(kù)驅(qū)動(dòng)連接由一 個(gè)錯(cuò)誤信息而導(dǎo)致失敗
  protected String poolPingQuery = "NO PING QUERY SET";
  //開(kāi)啟或禁用偵測(cè)查詢(xún)
  protected boolean poolPingEnabled = false;
  //用來(lái)配置 poolPingQuery 多次時(shí)間被用一次
  protected int poolPingConnectionsNotUsedFor = 0;
  private int expectedConnectionTypeCode;
}

這些字段主要記錄了連接池的重要信息:連接池大小、空閑時(shí)最大連接數(shù)、最大活躍連接數(shù)、超時(shí)時(shí)間等。而整整揭開(kāi)PooledDataSource獲取連接對(duì)象的神秘面紗還需要介紹兩個(gè)類(lèi)。PooledConnection和PoolState

PooledConnection

PooledConnection實(shí)現(xiàn)了InvocationHandler接口,他是用來(lái)做JDK動(dòng)態(tài)代理的。前文提到過(guò),mybatis使用JDK動(dòng)態(tài)代理重寫(xiě)了Connection對(duì)象的close方法,就是在該類(lèi)中實(shí)現(xiàn)的邏輯。該類(lèi)有幾個(gè)重要屬性。

  • private PooledDataSource dataSource; // dataSource的副本
  • private Connection realConnection; // 真實(shí)連接對(duì)象
  • private Connection proxyConnection; // 實(shí)際返回的代理對(duì)象

接下來(lái)來(lái)看下代理對(duì)象的invoke方法是如何重寫(xiě)close方法的。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  String methodName = method.getName();
  //如果調(diào)用close的話(huà),忽略它,反而將這個(gè)connection加入到池中
  if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
    dataSource.pushConnection(this);
    return null;
  } 
  return method.invoke(realConnection, args);
  // 其他邏輯省略....
}

在invoke方法中判斷下執(zhí)行的方法名稱(chēng)是否是Close,如果是,就不再執(zhí)行原來(lái)的close方法了,而是執(zhí)行PooledDataSource 的pushConnection方法!從方法名可以看出方法的作用是:把連接push到連接池PooledDataSource 中。pushConnection的邏輯后文詳細(xì)說(shuō)明

PoolState

上文提到PooledDataSource并不管理連接對(duì)象。那么程序初始化的時(shí)候創(chuàng)建的一批連接存放到哪里了呢?答案是存在PoolState對(duì)象中,而PooledDataSource有一個(gè)屬性就是PoolState。也就是說(shuō)PooledDataSource是通過(guò)PoolState來(lái)管理連接池的。

一批連接在Java中就是一個(gè)List集合嘛。那么我們想一下PoolState都需要怎么管理連接呢?首先根據(jù)連接的狀態(tài),可以把連接分為2種

  • 空閑連接protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
  • 活躍連接protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();

PoolState中兩個(gè)List屬性分別存儲(chǔ)空閑連接和活躍連接。需要連接的時(shí)候就從idleConnections 列表中取,關(guān)聯(lián)連接時(shí)就把連接從activeConnections 中移到idleConnections 中。

PoolState中還有一些其他的統(tǒng)計(jì)信息字段,比如 請(qǐng)求次數(shù)、請(qǐng)求的總時(shí)間、總連接數(shù)等這些屬性比較簡(jiǎn)單就不再列出了

獲取連接

介紹完P(guān)ooledConnection和PoolState這兩個(gè)類(lèi)后,我們來(lái)看下PooledDataSource是怎么獲取連接的。獲取連接的邏輯在PooledDataSource#getConnection方法中,getConnection方法只是一個(gè)殼子,具體調(diào)用邏輯在popConnection方法。我們來(lái)看一下(我只列出了重要邏輯)

public Connection getConnection() throws SQLException {
  return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
private PooledConnection popConnection(String username, String password) throws SQLException {
  //最外面是while死循環(huán),如果一直拿不到connection,則不斷嘗試
  while (conn == null) {
    synchronized (state) {
      if (!state.idleConnections.isEmpty()) {
        //如果有空閑的連接的話(huà),返回第一個(gè)空閑連接
        conn = state.idleConnections.remove(0);
      } else {
        //如果沒(méi)有空閑的連接
        if (state.activeConnections.size() &lt; poolMaximumActiveConnections) {
          //如果activeConnections太少,那就new一個(gè)PooledConnection
          conn = new PooledConnection(dataSource.getConnection(), this);
        } else {
          //如果activeConnections已經(jīng)很多了,那不能再new了
          //取得activeConnections列表的第一個(gè)(最老的)
          PooledConnection oldestActiveConnection = state.activeConnections.get(0);
          long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
          if (longestCheckoutTime &gt; poolMaximumCheckoutTime) {
            //如果checkout時(shí)間過(guò)長(zhǎng),則這個(gè)connection標(biāo)記為overdue(過(guò)期)
            //刪掉最老的連接,然后再new一個(gè)新連接
            conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
            oldestActiveConnection.invalidate();
          } else {
            //如果checkout時(shí)間不夠長(zhǎng),沒(méi)辦法,只能等待,在此分支會(huì)記錄一些統(tǒng)計(jì)信息
          }
        }
      }
      if (conn != null) {
        if (conn.isValid()) {
          //如果已經(jīng)拿到connection,則記錄一些統(tǒng)計(jì)信息
        } else {
          //如果沒(méi)拿到,統(tǒng)計(jì)信息:壞連接+1
          state.badConnectionCount++;
          localBadConnectionCount++;
          conn = null;
          //如果好幾次都拿不到,就放棄了,拋出異常
        }
      }
    }
  }
  return conn;
}

在popConnection中

  • 從PoolState對(duì)象的空閑連接列表中獲取連接,如果有空閑連接就返回。
  • 從PoolState對(duì)象的活躍連接列表中獲取連接,如果連接數(shù)小于最大活躍數(shù),則new一個(gè)連接返回。如果沒(méi)有只能等待其他線(xiàn)程釋放連接再進(jìn)行獲取
  • 無(wú)論是否獲取到連接,對(duì)連接進(jìn)行一些信息統(tǒng)計(jì)并記錄到PoolState對(duì)象中。一旦嘗試獲取連接的時(shí)間超過(guò)了閾值,就會(huì)放棄獲取連接拋出異常

關(guān)閉連接

在PooledConnection小節(jié)中見(jiàn)到,PooledConnection重寫(xiě)了Connection的close方法。當(dāng)調(diào)用Connection的close方法時(shí)真正執(zhí)行的邏輯是PooledDataSource的pushConnection方法。該代碼邏輯很簡(jiǎn)單,大體上說(shuō),就是把連接從活躍列表中刪除,加入到空閑列表中。具體實(shí)現(xiàn)如下

protected void pushConnection(PooledConnection conn) throws SQLException {
  synchronized (state) {
    //先從activeConnections中刪除此connection
    state.activeConnections.remove(conn);
    if (conn.isValid()) {
      if (state.idleConnections.size() &lt; poolMaximumIdleConnections &amp;&amp; conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
        //如果空閑的連接太少,
        state.accumulatedCheckoutTime += conn.getCheckoutTime();
        if (!conn.getRealConnection().getAutoCommit()) {
          conn.getRealConnection().rollback();
        }
        //new一個(gè)新的Connection,加入到idle列表
        PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
        state.idleConnections.add(newConn);
        //通知其他線(xiàn)程可以來(lái)?yè)宑onnection了
        state.notifyAll();
      } else {
       //否則,即空閑的連接已經(jīng)足夠了
        state.accumulatedCheckoutTime += conn.getCheckoutTime();
        //那就將connection關(guān)閉就可以了,獲取真正的connection對(duì)象并且關(guān)閉
        conn.getRealConnection().close();
        conn.invalidate();
      }
    } 
  }
}

關(guān)閉過(guò)程:

  • 空閑連接數(shù)<最大空閑連接數(shù) 則新建一個(gè)連接存放到PoolState的空閑列表中并通知其他線(xiàn)程可以來(lái)?yè)孋onnection對(duì)象
  • 如果PoolState的空閑列表是滿(mǎn)的,那只能獲取真正的connection對(duì)象并將其關(guān)閉了。

小結(jié)

  • PooledDataSource真正意義上實(shí)現(xiàn)了DataSource接口。具有連接池的意義
  • PooledDataSource通過(guò)PooledConnection和PoolState來(lái)管理連接池中的連接
  • PooledConnection重寫(xiě)了Connection對(duì)象的close方法。調(diào)用Connection的close方法時(shí)并不會(huì)真正的關(guān)閉連接,而是先要進(jìn)行歸還連接的操作。
  • PoolState是對(duì)連接列表狀態(tài)的管理。它有兩個(gè)List屬性,分別存儲(chǔ)了活躍連接列表空閑連接列表

DataSourceFactory

獲取MyBatis提供的DataSource實(shí)現(xiàn),需要通過(guò)工廠(chǎng)DataSourceFactory接口來(lái)獲取。在這里MyBatis使用了工廠(chǎng)方法模式。DataSourceFactory有兩個(gè)實(shí)現(xiàn)類(lèi)。分別是

  • UnpooledDataSourceFactory
  • PooledDataSourceFactory

我們首先來(lái)看下工廠(chǎng)接口定義

public interface DataSourceFactory {
  //設(shè)置屬性,被XMLConfigBuilder所調(diào)用
  void setProperties(Properties props);
  //生產(chǎn)數(shù)據(jù)源,直接得到j(luò)avax.sql.DataSource
  DataSource getDataSource();
}

其中最重要的方法就是getDataSource,它很直觀,通過(guò)工廠(chǎng)對(duì)象的該方法可以獲取DataSource實(shí)現(xiàn)。

UnpooledDataSourceFactory

UnpooledDataSourceFactory獲取dataSource的方法非常簡(jiǎn)單直觀。

首先,構(gòu)造方法里里new了一個(gè)UnpooledDataSource對(duì)象存放到工廠(chǎng)的屬性中

然后,getDataSource直接返回該對(duì)象即可。具體實(shí)現(xiàn)如下

public class UnpooledDataSourceFactory implements DataSourceFactory {
  protected DataSource dataSource;
  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }
  public DataSource getDataSource() {
    return dataSource;
  }
}

PooledDataSourceFactory

PooledDataSourceFactory就有意思了,想偷懶,直接繼承自UnpooledDataSourceFactory。只需要在構(gòu)造方法中new一個(gè)PooledDataSource對(duì)象,再通過(guò)getDataSource方法獲取即可。

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  //數(shù)據(jù)源換成了PooledDataSource
  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }
}

結(jié)語(yǔ)

個(gè)人感覺(jué)mybatis提供的DataSourceFactory的實(shí)現(xiàn)類(lèi)有點(diǎn)雞肋??梢哉f(shuō)還是new對(duì)象。我們知道工廠(chǎng)模式創(chuàng)建的一般都是比較復(fù)雜的對(duì)象,是用來(lái)幫助開(kāi)發(fā)者屏蔽復(fù)雜的細(xì)節(jié)。而mybatis的這兩個(gè)實(shí)現(xiàn)都只是new對(duì)象而已。

以上就是MyBatis基礎(chǔ)支持DataSource實(shí)現(xiàn)源碼解析的詳細(xì)內(nèi)容,更多關(guān)于MyBatis基礎(chǔ)支持DataSource的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java響應(yīng)式編程之Reactor使用示例解析

    java響應(yīng)式編程之Reactor使用示例解析

    這篇文章主要為大家介紹了java響應(yīng)式編程之Reactor使用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • MyBatis中關(guān)于SQL的寫(xiě)法總結(jié)

    MyBatis中關(guān)于SQL的寫(xiě)法總結(jié)

    這篇文章主要介紹了MyBatis中關(guān)于SQL的寫(xiě)法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Spring5+SpringMvc+Hibernate5整合的實(shí)現(xiàn)

    Spring5+SpringMvc+Hibernate5整合的實(shí)現(xiàn)

    這篇文章主要介紹了Spring5+SpringMvc+Hibernate5整合的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Maven打jar包的三種方式(小結(jié))

    Maven打jar包的三種方式(小結(jié))

    這篇文章主要介紹了Maven打jar包的三種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 詳解Spring Cloud Consul 實(shí)現(xiàn)服務(wù)注冊(cè)和發(fā)現(xiàn)

    詳解Spring Cloud Consul 實(shí)現(xiàn)服務(wù)注冊(cè)和發(fā)現(xiàn)

    這篇文章主要介紹了Spring Cloud Consul 實(shí)現(xiàn)服務(wù)注冊(cè)和發(fā)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • Java環(huán)境變量配置教程

    Java環(huán)境變量配置教程

    這篇文章主要介紹了Java環(huán)境變量配置教程,簡(jiǎn)單介紹了java的環(huán)境變量設(shè)置方法,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Java實(shí)現(xiàn)正則匹配 “1234567” 這個(gè)字符串出現(xiàn)四次或四次以上

    Java實(shí)現(xiàn)正則匹配 “1234567” 這個(gè)字符串出現(xiàn)四次或四次以上

    文章介紹了如何在Java中使用正則表達(dá)式匹配一個(gè)字符串四次或四次以上的出現(xiàn),首先創(chuàng)建正則表達(dá)式,然后使用Pattern和Matcher類(lèi)進(jìn)行匹配和計(jì)數(shù),通過(guò)示例代碼展示了如何實(shí)現(xiàn)這一功能,并解釋了匹配的整體次數(shù)和精確出現(xiàn)次數(shù)的邏輯,感興趣的朋友一起看看吧
    2025-02-02
  • Java NIO無(wú)法綁定指定IP和端口解決方案

    Java NIO無(wú)法綁定指定IP和端口解決方案

    這篇文章主要介紹了Java NIO無(wú)法綁定指定IP和端口解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java中SimpleDateFormat方法超詳細(xì)分析

    Java中SimpleDateFormat方法超詳細(xì)分析

    這篇文章主要給大家介紹了關(guān)于Java中SimpleDateFormat方法超詳細(xì)分析的相關(guān)資料,SimpleDateFormat 是一個(gè)以國(guó)別敏感的方式格式化和分析數(shù)據(jù)的具體類(lèi),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08
  • 使用JDBC實(shí)現(xiàn)數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象層(DAO)代碼示例

    使用JDBC實(shí)現(xiàn)數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象層(DAO)代碼示例

    這篇文章主要介紹了使用JDBC實(shí)現(xiàn)數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象層(DAO)代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-10-10

最新評(píng)論

新昌县| 金乡县| 调兵山市| 化州市| 蓝田县| 双辽市| 仙居县| 郧西县| 曲沃县| 新民市| 澄江县| 梅州市| 绥化市| 怀安县| 陕西省| 安阳市| 阜新| 久治县| 班玛县| 林甸县| 山阴县| 壤塘县| 新巴尔虎左旗| 古田县| 高雄市| 古田县| 观塘区| 沛县| 贞丰县| 张家港市| 呼伦贝尔市| 克东县| 金平| 英超| 抚州市| 崇左市| 和静县| 军事| 九龙城区| 永寿县| 新田县|