欢迎来到徐庆高(Tea)的个人博客网站
磨难很爱我,一度将我连根拔起。从惊慌失措到心力交瘁,我孤身一人,但并不孤独无依。依赖那些依赖我的人,信任那些信任我的人,帮助那些给予我帮助的人。如果我愿意,可以分裂成无数面镜子,让他们看见我,就像看见自己。察言观色和模仿学习是我的领域。像每个深受创伤的人那样,最终,我学会了随遇而安。
当前位置: 日志文章 > 详细内容

SpringJDBC源码初探之DataSource类详解

2025年08月16日 Java
一、datasource接口核心作用datasource是jdbc规范的核心接口,位于javax.sql包中,用于替代传统的drivermanager获取数据库连接。spring框架通过org.spr

一、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
│   ├── 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 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)

@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 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);
    
    @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();
    }
}

特点:

  • 根据事务隔离级别选择数据源
  • 支持通过整数或字符串常量配置隔离级别

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。