开发者

SpringJDBC源码初探之DataSource类详解

开发者 https://www.devze.com 2025-08-16 10:20 出处:网络 作者: yifanghub
目录一、DataSource接口核心作用二、DataSource源码分析1. DriverManagerDataSource核心方法2. SingleConnectionDataSource方法3. AbstractRoutingDataSource4. IsolationLevelDataSourceRouter(基于事务隔离级别的
目录
  • 一、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)。

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号