单纯的DruidDataSource 不是拿来即用,在很多企业级 Java 项目中,我们需要扩展,今天主要介绍一下我们功能强大的扩展的KmssDruidDataSource,他是如何解耦,可扩展的实现我们适配各个数据库的监控 ,现在我们完整介绍它的设计目的与实现思路。
一、KmssDruidDataSource 是做什么的?
KmssDruidDataSource 是一个 继承自 DruidDataSource 的增强型数据源实现,主要解决以下问题:
-
不同数据库方言下的连接校验差异
-
validationQuery 遗漏导致的断链不可恢复
-
JDBC 连接泄露难以定位
-
多启动方式(JDBC / JNDI)下的初始化兼容
一句话概括:
它不是替代 Druid,而是让 Druid 更适合企业长期运行。
二、整体类结构与生命周期位置
public class KmssDruidDataSource extends DruidDataSource
implements InitializingBean
关键点说明
-
继承
DruidDataSource:完全复用 Druid 能力 -
实现
InitializingBean:在 Spring 完成属性注入后、数据源真正初始化前 插入增强逻辑
这是非常典型的 Spring + 中间件增强模式。
三、启用条件控制:不是所有场景都生效
private boolean checkNecessary() {
return KMSS_TYPE_JDBC.equals(KMSS_CONNECTION_TYPE)
&& "druidDataSource".equalsIgnoreCase(
ResourceUtil.getKmssConfigString("kmss.jdbc.datasource"));
}
为什么要做条件判断?
在真实项目中,往往存在:
-
JNDI 数据源
-
多数据源并存
-
历史系统混合配置
增强逻辑必须是"可控的",否则很容易引入隐性风险。
四、初始化阶段的核心增强逻辑
增强逻辑集中在 afterPropertiesSet() 中。
4.1 自动识别数据库方言
当未显式配置 validationQuery 时,系统会:
-
读取
hibernate.dialect -
使用
ServiceLoader加载IDataBaseAdapter -
找到匹配的数据库适配器
ServiceLoader<IDataBaseAdapter> loaders =
ServiceLoader.load(IDataBaseAdapter.class);
设计意义
-
解耦数据库差异
-
避免硬编码 SQL
-
支持扩展更多数据库类型
4.2 自动设置 validationQuery(兜底机制)
setValidationQuery(adapter.getConnectionTestQuery());
典型效果:
| 数据库 | 校验 SQL |
|---|---|
| Oracle | select 1 from dual |
| MySQL | select 1 |
如果识别失败,会输出 明确的 WARN 日志,提示人工配置。
这是一个非常务实的设计 :
能自动就自动,不能自动就明确告警。
4.3 HibernateContext 数据库适配器注入
HibernateContext.setDataBaseAdapter(dbAdapter);
这一步不仅影响连接校验,还会影响:
-
SQL 生成
-
分页语法
-
数据库函数适配
说明 KmssDruidDataSource 在系统中承担的是数据库上下文初始化角色。
五、Druid 统计与日志增强
当配置开启:
kmss.jdbc.stat.enabled=true
初始化阶段会:
addFilters("stat");
super.filters.add(new KmssDruidDataSourceLogFilter());
带来的能力
-
SQL 执行统计
-
活跃连接数监控
-
自定义 JDBC 日志输出
为后续性能分析和问题定位打下基础。
六、连接占用监控:最有工程价值的设计
6.1 设计动机
现实中的 JDBC 问题往往是:
-
连接没关
-
事务太长
-
线程被阻塞
但日志里只剩一句:
获取数据库连接超时
6.2 实现方式
KmssDruidDataSource 启动一个定时任务:
-
每 60 秒扫描一次
-
找出占用超过 60 秒的连接
-
Dump 持有连接的线程堆栈
info.dumpActiveConnectionStackTrace(60000);
6.3 实际价值
-
不依赖问题复现
-
不需要 JVM 全量 dump
-
直接定位"是谁没释放连接"
对于 OA、流程、审批类系统尤为重要。
七、JNDI 场景下的 init 兼容处理
@Override
public void init() throws SQLException {
if (checkNecessary()) {
super.init();
}
}
解决的问题
-
容器托管数据源提前初始化
-
非目标场景误触发 Druid init
-
启动阶段异常
这是典型的 企业级启动兼容代码。
八、KmssDruidDataSource 的设计特点总结
优点
-
启用条件明确,不侵入
-
数据库方言解耦
-
自动兜底 + 明确告警
-
具备连接级可观测能力
适用场景
-
多数据库支持系统
-
长事务 / 流程型系统
-
对稳定性要求高的企业应用
九、结语
扩展KmssDruidDataSource 主要有如下功能:
-
出问题时,你能不能快速定位
-
跑久了,系统会不会慢慢失血