Spring-JDBC 源碼學習(2) —— DataSource

Spring JDBC 源碼學習

DataSource

上一節在粗略地瞭解了 JdbcTemplate 提供的方法之後,下面先來對 DataSource 做一點了解。

Java 提供的 DataSource 定義

DataSource 是 Java 核心庫提供的接口。位於 javax.sql package 下。

DataSource 接口可以被視作是一個提供物理 DB 實例連接(Connection) 的工廠,通過 DataSource 持有的各種屬性(包括 DB Url, Username, Password 等)來獲取一個連接(Connection) 。DataSource 接口有三種不同的實現方案:

  1. 最基本的實現——生產一個標準連接(Connection) 對象
  2. 連接池方案——生產會被自動添加到連接池的對象
  3. 分佈式事物實現——生產一個可以支持分佈式事物,並默認被添加到連接池的連接對象

包括兩個對外提供連接(Connection) 對象的方法,

Connection getConnection() throws SQLException;
Connection getConnection(String username, String password) throws SQLException;

其父接口 CommonDataSource 提供設置/獲取 LogWriter,登錄 DB 超時時間和獲取父 Logger 的方法。

Spring-JDBC 擴展的 DataSource 定義

在 Spring-jdbc 下,DataSources 最頂級的類是 AbstractDataSource ,對 DataSource 的所有父接口方法做了實現。但保留 getConnection() 方法由子類實現。

在 AbstractDriverBasedDataSource 中,定義了大量的參數,諸如 url, username 等,這些都被用來定位並定義與數據庫實例的連接。

public abstract class AbstractDriverBasedDataSource extends AbstractDataSource {

   @Nullable
   private String url;

   @Nullable
   private String username;

   @Nullable
   private String password;

   @Nullable
   private String catalog;

   @Nullable
   private String schema;

   @Nullable
   // 可以看到此處有一個 Properties 類
   private Properties connectionProperties;

   // 省略若干方法

    @Override
    public Connection getConnection() throws SQLException {
        // 調用內部方法 getConnectionFromDriver()
        return getConnectionFromDriver(getUsername(), getPassword());
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        // 調用內部方法 getConnectionFromDriver()
        return getConnectionFromDriver(username, password);
    }

   // 定義了一個獲取 Connection 的方法,由 getConnection() 方法調用,
   // 此方法主要是將屬性做了一個整合
   // 具體獲取 Connection 的邏輯仍然下放到子類實現 見 40 行
   protected Connection getConnectionFromDriver(@Nullable String username, @Nullable String password) throws SQLException {
        Properties mergedProps = new Properties();
        Properties connProps = getConnectionProperties();
        if (connProps != null) {
            mergedProps.putAll(connProps);
        }
        if (username != null) {
            mergedProps.setProperty("user", username);
        }
        if (password != null) {
            mergedProps.setProperty("password", password);
        }

        // 獲取 Connection 邏輯下放
        Connection con = getConnectionFromDriver(mergedProps);
        if (this.catalog != null) {
            con.setCatalog(this.catalog);
        }
        if (this.schema != null) {
            con.setSchema(this.schema);
        }
        return con;
    }

    // 該類中獲取 Connection 的方法是抽象方法
    protected abstract Connection getConnectionFromDriver(Properties props) throws SQLException;

}

整合方案爲將除 url 外的所有參數整合在同一個 Properties 對象中 (其中,Properties 可以被認爲是一個線程安全的 Hash Map) 。最終調用 Connection getConnectionFromDriver(Properties props) 獲取連接。

AbstractDriverBasedDataSource 抽象類的兩個子類 DriverManagerDataSource 和 SimpleDriverDataSource 都以不同方式獲得了連接(Connection),但總結而言,獲取連接(Connection) 的任務被委託給了 Driver 來實現。

// ----------------------------
// SimpleDriverDataSource 的實現
// ----------------------------
@Override
protected Connection getConnectionFromDriver(Properties props) throws SQLException {
    Driver driver = getDriver();
    String url = getUrl();
    Assert.notNull(driver, "Driver must not be null");
    if (logger.isDebugEnabled()) {
        logger.debug("Creating new JDBC Driver Connection to [" + url + "]");
    }
    // 哈哈,重點在這... driver 在該類中被預先注入
    return driver.connect(url, props);
}

// -----------------------------
// DriverManagerDataSource 的實現
// -----------------------------
@Override
protected Connection getConnectionFromDriver(Properties props) throws SQLException {
    String url = getUrl();
    Assert.state(url != null, "'url' not set");
    if (logger.isDebugEnabled()) {
        logger.debug("Creating new JDBC DriverManager Connection to [" + url + "]");
    }
    // 調了個內部函數
    return getConnectionFromDriverManager(url, props);
}

protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException {
    // 委託給 DriverManager 類來獲取連接
    // DriverManager 的主要操作是遍歷在該管理類中註冊的 Driver
    // 每個 Driver 實例都去嘗試一下,能不能獲得一個連接
    // 第一次在某個 Driver 中拿到一個連接即返回連接 (Connection)
    return DriverManager.getConnection(url, props);
}

簡要的類圖如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章