Spring JDBC 源碼學習
DataSource
上一節在粗略地瞭解了 JdbcTemplate 提供的方法之後,下面先來對 DataSource 做一點了解。
Java 提供的 DataSource 定義
DataSource 是 Java 核心庫提供的接口。位於 javax.sql package 下。
DataSource 接口可以被視作是一個提供物理 DB 實例連接(Connection) 的工廠,通過 DataSource 持有的各種屬性(包括 DB Url, Username, Password 等)來獲取一個連接(Connection) 。DataSource 接口有三種不同的實現方案:
- 最基本的實現——生產一個標準連接(Connection) 對象
- 連接池方案——生產會被自動添加到連接池的對象
- 分佈式事物實現——生產一個可以支持分佈式事物,並默認被添加到連接池的連接對象
包括兩個對外提供連接(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);
}
簡要的類圖如下: