当前位置: 代码网 > it编程>编程语言>Java > SpringJDBC源码初探之DataSource类详解

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

特点:

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

总结

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com