一、DataSource接口核心作用
DataSource是JDBC規范的核心接口,位于javax.sql包中,用于替代傳統的DriverManager獲取數據庫連接。Spring框架通過org.springframework.jdbc.datasource包對該接口進行了增強,提供連接池管理、事務綁定等高級特性。
二、DataSource源碼分析
核心接口javax.sql.DataSource
public interface DataSource extends CommonDataSource, Wrapper {// 獲取數據庫連接Connection getConnection() throws SQLException;// 使用憑證獲取連接Connection getConnection(String username, String password)throws SQLException;
}
可以看到,DataSource接口提供了獲取連接的的方法,并且DataSource繼承了兩個父接口CommonDataSource和Wrapper,CommonDataSource定義如下:
public interface CommonDataSource {// 獲取日志記錄器PrintWriter getLogWriter() throws SQLException;// 設置日志記錄器void setLogWriter(PrintWriter out) throws SQLException;// 設置登錄超時時間(秒)void setLoginTimeout(int seconds) throws SQLException;// 獲取登錄超時時間int getLoginTimeout() throws SQLException;// 獲取父Loggerdefault Logger getParentLogger() throws SQLFeatureNotSupportedException {throw new SQLFeatureNotSupportedException();}
}
這里CommonDataSource 提供了獲取和設置日志的方法,連接超時管理以及獲取父Logger的方法。
public interface Wrapper {// 檢查是否實現指定接口boolean isWrapperFor(Class<?> iface) throws SQLException;// 獲取接口實現<T> T unwrap(Class<T> iface) throws SQLException;
}
Wrapper主要用于獲取特定擴展功能
AbstractDataSource抽象類,主要提供DataSource接口中的某些方法(如getLoginTimeout()、setLoginTimeout(int)等)的默認實現
主要的繼承關系如下
AbstractDataSource
├── AbstractDriverBasedDataSource
│ ├── DriverManagerDataSource
│ └── SimpleDriverDataSource
├── AbstractRoutingDataSource└──IsolationLevelDataSourceRouter
- DriverManagerDataSource核心方法
public class DriverManagerDataSource extends AbstractDriverBasedDataSource {@Overrideprotected Connection getConnectionFromDriver(String username, 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);}// 關鍵點:每次通過DriverManager新建連接return DriverManager.getConnection(getUrl(), mergedProps);}
}
說明:通過用戶名密碼從驅動獲取連接,每次調用 getConnection() 都創建一條新連接,無連接池功能,適合測試環境。
2. SingleConnectionDataSource方法
public class SingleConnectionDataSource extends AbstractDriverBasedDataSource {private volatile Connection connection;@Overrideprotected Connection getConnectionFromDriver(String username, String password) throws SQLException {synchronized (this) {if (this.connection == null) {// 初始化唯一連接this.connection = doGetConnection(username, password);}return this.connection;}}protected Connection doGetConnection(String username, String password) throws SQLException {// 實際創建連接邏輯Properties mergedProps = new Properties();// ...屬性合并邏輯與DriverManagerDataSource類似return DriverManager.getConnection(getUrl(), mergedProps);}
}
說明:單例模式來維護唯一連接,直接使用JDBC Driver實例,線程安全通過synchronized和volatile保證。
3.AbstractRoutingDataSource
AbstractRoutingDataSource
實現動態數據源路由抽象類,主要屬性如下
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {// 目標數據源映射表private Map<Object, Object> targetDataSources;// 默認數據源private Object defaultTargetDataSource;// 解析后的數據源映射表private Map<Object, DataSource> resolvedDataSources;// 解析后的默認數據源private DataSource resolvedDefaultDataSource;// 數據源查找接口private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();// 是否寬松回退到默認數據源private boolean lenientFallback = true;
}
初始化方法(afterPropertiesSet
)
@Overridepublic void afterPropertiesSet() {if (this.targetDataSources == null) {throw new IllegalArgumentException("Property 'targetDataSources' is required");}this.resolvedDataSources = CollectionUtils.newHashMap(this.targetDataSources.size());this.targetDataSources.forEach((key, value) -> {Object lookupKey = resolveSpecifiedLookupKey(key);DataSource dataSource = resolveSpecifiedDataSource(value);this.resolvedDataSources.put(lookupKey, dataSource);});if (this.defaultTargetDataSource != null) {this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);}}
說明:將配置的targetDataSources
轉換為可用的resolvedDataSources
獲取連接的邏輯:
@Override
public Connection getConnection() throws SQLException {return determineTargetDataSource().getConnection();
}
protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");// 獲取當前查找鍵Object lookupKey = determineCurrentLookupKey();// 根據鍵查找數據源DataSource dataSource = this.resolvedDataSources.get(lookupKey);// 回退到默認數據源if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");}return dataSource;
}
AbstractRoutingDataSource
定義了determineCurrentLookupKey()
抽象方法,子類僅需實現此方法提供鍵值獲取邏輯。
核心邏輯:
- 初始化階段:
- . 實現
InitializingBean
接口,在afterPropertiesSet()
中解析targetDataSources
,生成resolvedDataSources
- 將
defaultTargetDataSource
解析為resolvedDefaultDataSource
- 運行時路由:
- 通過
determineCurrentLookupKey()
抽象方法獲取當前數據源標識 - 根據標識從
resolvedDataSources
中查找對應的數據源 - 未找到時根據
lenientFallback
決定是否使用默認數據源
4.IsolationLevelDataSourceRouter(基于事務隔離級別的路由)
public class IsolationLevelDataSourceRouter extends AbstractRoutingDataSource {private static final Constants constants = new Constants(TransactionDefinition.class);@Overrideprotected Object resolveSpecifiedLookupKey(Object lookupKey) {// 解析隔離級別配置if (lookupKey instanceof Integer) return lookupKey;if (lookupKey instanceof String) {String constantName = (String) lookupKey;if (!constantName.startsWith(DefaultTransactionDefinition.PREFIX_ISOLATION)) {throw new IllegalArgumentException("Only isolation constants allowed");}return constants.asNumber(constantName);}throw new IllegalArgumentException("Invalid lookup key");}@Overrideprotected Object determineCurrentLookupKey() {// 從當前事務同步管理器中獲取隔離級別return TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();}
}
特點:
- 根據事務隔離級別選擇數據源
- 支持通過整數或字符串常量配置隔離級別