一、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(); } }
特点:
- 根据事务隔离级别选择数据源
- 支持通过整数或字符串常量配置隔离级别
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论