SpringJDBC源碼初探之DataSource類詳解
一、DataSource接口核心作用
DataSource是JDBC規(guī)范的核心接口,位于javax.sql包中,用于替代傳統(tǒng)的DriverManager獲取數(shù)據(jù)庫連接。
Spring框架通過org.springframework.jdbc.datasource包對(duì)該接口進(jìn)行了增強(qiáng),提供連接池管理、事務(wù)綁定等高級(jí)特性。
二、DataSource源碼分析
核心接口javax.sql.DataSource
public interface DataSource extends CommonDataSource, Wrapper {
// 獲取數(shù)據(jù)庫連接
Connection getConnection() throws SQLException;
// 使用憑證獲取連接
Connection getConnection(String username, String password)
throws SQLException;
}
可以看到,DataSource接口提供了獲取連接的的方法,并且DataSource繼承了兩個(gè)父接口CommonDataSource和Wrapper,CommonDataSource定義如下:
public interface CommonDataSource {
// 獲取日志記錄器
PrintWriter getLogWriter() throws SQLException;
// 設(shè)置日志記錄器
void setLogWriter(PrintWriter out) throws SQLException;
// 設(shè)置登錄超時(shí)時(shí)間(秒)
void setLoginTimeout(int seconds) throws SQLException;
// 獲取登錄超時(shí)時(shí)間
int getLoginTimeout() throws SQLException;
// 獲取父Logger
default Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
}
這里CommonDataSource 提供了獲取和設(shè)置日志的方法,連接超時(shí)管理以及獲取父Logger的方法。
public interface Wrapper {
// 檢查是否實(shí)現(xiàn)指定接口
boolean isWrapperFor(Class<?> iface) throws SQLException;
// 獲取接口實(shí)現(xiàn)
<T> T unwrap(Class<T> iface) throws SQLException;
}
Wrapper主要用于獲取特定擴(kuò)展功能
AbstractDataSource抽象類,主要提供DataSource接口中的某些方法(如getLoginTimeout()、setLoginTimeout(int)等)的默認(rèn)實(shí)現(xiàn)
主要的繼承關(guān)系如下:
AbstractDataSource
├── AbstractDriverBasedDataSource
│ ├── DriverManagerDataSource
│ └── SimpleDriverDataSource
├── AbstractRoutingDataSource
└──IsolationLevelDataSourceRouter
1. DriverManagerDataSource核心方法
public class DriverManagerDataSource extends AbstractDriverBasedDataSource {
@Override
protected 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);
}
// 關(guān)鍵點(diǎn):每次通過DriverManager新建連接
return DriverManager.getConnection(getUrl(), mergedProps);
}
}
說明:通過用戶名密碼從驅(qū)動(dòng)獲取連接,每次調(diào)用 getConnection() 都創(chuàng)建一條新連接,無連接池功能,適合測(cè)試環(huán)境。
2. SingleConnectionDataSource方法
public class SingleConnectionDataSource extends AbstractDriverBasedDataSource {
private volatile Connection connection;
@Override
protected 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 {
// 實(shí)際創(chuàng)建連接邏輯
Properties mergedProps = new Properties();
// ...屬性合并邏輯與DriverManagerDataSource類似
return DriverManager.getConnection(getUrl(), mergedProps);
}
}
說明:?jiǎn)卫J絹砭S護(hù)唯一連接,直接使用JDBC Driver實(shí)例,線程安全通過synchronized和volatile保證。
3. AbstractRoutingDataSource
AbstractRoutingDataSource實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源路由抽象類,主要屬性如下
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
// 目標(biāo)數(shù)據(jù)源映射表
private Map<Object, Object> targetDataSources;
// 默認(rèn)數(shù)據(jù)源
private Object defaultTargetDataSource;
// 解析后的數(shù)據(jù)源映射表
private Map<Object, DataSource> resolvedDataSources;
// 解析后的默認(rèn)數(shù)據(jù)源
private DataSource resolvedDefaultDataSource;
// 數(shù)據(jù)源查找接口
private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
// 是否寬松回退到默認(rèn)數(shù)據(jù)源
private boolean lenientFallback = true;
}
初始化方法(afterPropertiesSet)
@Override
public 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轉(zhuǎn)換為可用的resolvedDataSources
獲取連接的邏輯:
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
// 獲取當(dāng)前查找鍵
Object lookupKey = determineCurrentLookupKey();
// 根據(jù)鍵查找數(shù)據(jù)源
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
// 回退到默認(rèn)數(shù)據(jù)源
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()抽象方法,子類僅需實(shí)現(xiàn)此方法提供鍵值獲取邏輯。
核心邏輯:
初始化階段:
- 實(shí)現(xiàn)
InitializingBean接口,在afterPropertiesSet()中解析targetDataSources,生成resolvedDataSources - 將
defaultTargetDataSource解析為resolvedDefaultDataSource
運(yùn)行時(shí)路由:
- 通過
determineCurrentLookupKey()抽象方法獲取當(dāng)前數(shù)據(jù)源標(biāo)識(shí) - 根據(jù)標(biāo)識(shí)從
resolvedDataSources中查找對(duì)應(yīng)的數(shù)據(jù)源 - 未找到時(shí)根據(jù)
lenientFallback決定是否使用默認(rèn)數(shù)據(jù)源
4. IsolationLevelDataSourceRouter(基于事務(wù)隔離級(jí)別的路由)
public class IsolationLevelDataSourceRouter extends AbstractRoutingDataSource {
private static final Constants constants = new Constants(TransactionDefinition.class);
@Override
protected Object resolveSpecifiedLookupKey(Object lookupKey) {
// 解析隔離級(jí)別配置
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");
}
@Override
protected Object determineCurrentLookupKey() {
// 從當(dāng)前事務(wù)同步管理器中獲取隔離級(jí)別
return TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
}
}
特點(diǎn):
- 根據(jù)事務(wù)隔離級(jí)別選擇數(shù)據(jù)源
- 支持通過整數(shù)或字符串常量配置隔離級(jí)別
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot + Druid + Dynamic Datasource 多數(shù)據(jù)源配置方案
- SpringBoot中DataSource配置失敗問題的解決方法
- SpringBoot多數(shù)據(jù)源解決方案:dynamic-datasource-spring-boot-starter
- SpringBoot利用dynamic-datasource-spring-boot-starter解決多數(shù)據(jù)源問題
- 解決創(chuàng)建springboot后啟動(dòng)報(bào)錯(cuò):Failed?to?bind?properties?under‘spring.datasource‘
- SpringBoot框架DataSource多數(shù)據(jù)源配置方式
相關(guān)文章
SpringBoot 攔截器和自定義注解判斷請(qǐng)求是否合法
這篇文章主要介紹了SpringBoot 攔截器和自定義注解判斷請(qǐng)求是否合法,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-12-12
SpringBoot使用@Validated處理校驗(yàn)的方法步驟
@Validated?注解的主要目的是啟用和利用?Spring?的驗(yàn)證框架,它可以用于類上也可以用于方法參數(shù)上,本文給大家介紹了SpringBoot使用@Validated優(yōu)雅的處理校驗(yàn)的方法步驟,通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08
Java刪除指定文件夾下的所有內(nèi)容的方法(包括此文件夾)
下面小編就為大家?guī)硪黄狫ava刪除指定文件夾下的所有內(nèi)容的方法(包括此文件夾) 。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12
mybatis解決<foreach>標(biāo)簽不能超過1000的問題
MyBatis是一個(gè)開源的持久層框架,它可以幫助開發(fā)者簡(jiǎn)化數(shù)據(jù)庫操作的編寫,而foreach是MyBatis中的一個(gè)重要標(biāo)簽,用于在SQL語句中進(jìn)行循環(huán)操作,本文主要給大家介紹了mybatis解決<foreach>標(biāo)簽不能超過1000的問題,需要的朋友可以參考下2024-05-05
MybatisPlus字段自動(dòng)填充失效,填充值為null的解決方案
這篇文章主要介紹了MybatisPlus字段自動(dòng)填充失效,填充值為null的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
SpringCache緩存抽象之CacheManager與自定義鍵生成方式
本文將深入探討Spring Cache的核心組件CacheManager及自定義鍵生成策略,幫助開發(fā)者掌握緩存配置與優(yōu)化技巧,從而構(gòu)建高效可靠的緩存系統(tǒng),希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
基于springboot bean的實(shí)例化過程和屬性注入過程
這篇文章主要介紹了基于springboot bean的實(shí)例化過程和屬性注入過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

