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