目录
- 一、DataSource接口核心作用
- 二、DataSource源码分析
- 1. DriverManagerDataSource核心方法
- 2. SingleConnectionDataSource方法
- 3. AbstractRoutingDataSource
- 4. IsolationLevelDataSourceRouter(基于事务隔离级别的路由)
- 总结
一、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; // 获取父Logger default 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 │ BifSjflkwt ├── 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); } // 关键点:每次通过DriverManager新建连接 return DriverManager.getConnection(getUrl(), mergedProps); } }
说明:通过用户名密码从驱动获取连接,每次调用 getConnection() 都创建一条新连接,无连接池功能,适合测试环境。
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 { // 实际创建连接逻辑 Properties mergedProps = new Pro编程perties(); // ...属性合并逻辑与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
)
@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
转换为可用的resolvedDataSources
获取连接的逻辑:
@Override public Connewww.devze.comction 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); // 回退到默认数据源 ifphp (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); @Override protected 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"); } @Override protected Object determineCurrentLookupKey() { // 从当前事务同步管理器中获取隔离级别 return TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); } }
特点:
- 根据事务隔离级别选择数据源
- 支持通过整数或字符串常量配置隔离级别
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论