动态驱动数据同步系统:运行时热加载JDBC驱动的架构设计
一、系统架构总览
┌─────────────────────────────────────────────────────────────────────────────┐
│ 前端展示层 │
│ Vue 2 + Element UI │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 驱动管理 │ │ 数据源管理 │ │ SQL配置 │ │ 任务监控 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│ REST API
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 服务层 (Spring Boot) │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Admin模块 │ │ Common模块 │ │ Dataflow模块 │ │
│ │ (管理后台) │ │ (核心逻辑) │ │ (数据同步) │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────────────┐ ┌──────────────────┐
│ 动态驱动模块 │ │ 动态数据源模块 │ │ 流程引擎模块 │
│ │ │ │ │ │
│ ClassLoader │ │ DynamicRoutingDataSource│ │ LiteFlow │
│ DriverShim │ │ Druid连接池 │ │ GraalJS │
│ 懒加载策略 │ │ 多数据源管理 │ │ 流程编排 │
└──────────────────┘ └──────────────────────────┘ └──────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据存储层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 主库 │ │ 目标库1 │ │ 目标库2 │ │ 目标库N │ │
│ │ (MySQL) │ │ (Oracle) │ │ (PostgreSQL)│ │ (国产数据库)│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
二、问题背景
在企业级数据集成场景中,连接异构数据库面临以下挑战:
| 问题 | 传统方案 | 动态驱动方案 |
|---|---|---|
| 驱动版本冲突 | 预打包所有版本,容易冲突 | 运行时按需加载,版本隔离 |
| 部署包臃肿 | 包含所有驱动JAR | 只打包核心框架,驱动按需上传 |
| 运维困难 | 新增数据库需重新打包 | Web界面上传驱动即可 |
| 国产化适配 | 每次适配都要改代码发版 | 上传驱动JAR即可支持 |
三、核心设计方案
3.1 动态类加载器(DynamicDriverClassLoader)
设计目标
在运行时加载第三方JDBC驱动,同时解决:
- ClassLoader泄漏:DriverManager持有Driver引用导致ClassLoader无法回收
- 线程上下文污染:影响LiteFlow等依赖ServiceLoader的框架
架构设计
┌─────────────────────────────────────────────────────────────────┐
│ DynamicDriverClassLoader │
│ (extends URLClassLoader) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 驱动缓存 (ConcurrentHashMap<Integer, ClassLoader>) │
│ ┌─────────┬─────────┬─────────┬─────────┬─────────┐ │
│ │ driverId│ driverId│ driverId│ driverId│ driverId│ │
│ │ = 1 │ = 2 │ = 3 │ = 4 │ = N │ │
│ └─────────┴─────────┴─────────┴─────────┴─────────┘ │
│ │
│ 核心机制: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. DriverShim包装器 │ │
│ │ - 包装实际Driver,避免DriverManager持有ClassLoader引用│ │
│ │ - 卸载时可被GC回收 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ 2. 线程ClassLoader管理 │ │
│ │ - 加载时临时切换到自定义ClassLoader │ │
│ │ - 加载完成后必须恢复原始ClassLoader │ │
│ │ - 避免污染后续ServiceLoader调用 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
3.2 动态驱动管理器(DynamicDriverManager)
设计目标
- 驱动生命周期管理(加载/卸载/重载)
- 按需懒加载,非启动时全量加载
- 避免循环依赖
加载策略
┌─────────────────────────────────────────────────────────────────┐
│ 驱动加载流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 数据源配置变更 │
│ │ │
│ ▼ │
│ 检查driverId是否已加载 ──── 是 ──▶ 直接使用缓存的ClassLoader │
│ │ │
│ 否 │
│ ▼ │
│ 从数据库查询驱动信息 │
│ │ │
│ ▼ │
│ 创建DynamicDriverClassLoader │
│ │ │
│ ▼ │
│ 加载驱动并注册到DriverManager │
│ │ │
│ ▼ │
│ 缓存ClassLoader,供后续使用 │
│ │
└─────────────────────────────────────────────────────────────────┘
3.3 动态数据源提供者(LeenledaDataSourceProvider)
设计目标
- 在所有Bean初始化完成后加载动态数据源
- 确保LiteFlow等框架已完全就绪
- 支持多种数据库的连接池配置
初始化时序
┌─────────────────────────────────────────────────────────────────┐
│ Spring容器启动时序 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 阶段1:Bean定义注册 │
│ ├─ 注册LeenledaDataSourceProvider (SmartInitializingSingleton)│
│ ├─ 注册DynamicDriverManager │
│ └─ 注册其他业务Bean │
│ │ │
│ ▼ │
│ 阶段2:Bean实例化和初始化 │
│ ├─ 实例化主库DataSource │
│ ├─ 实例化LiteFlow (CGLIB增强) │
│ └─ 实例化其他Bean │
│ │ │
│ ▼ │
│ 阶段3:所有单例Bean初始化完成 │
│ └─ 调用afterSingletonsInstantiated() │
│ │ │
│ ▼ │
│ 阶段4:加载动态数据源 │
│ ├─ 查询数据库获取数据源配置 │
│ ├─ 按需加载驱动ClassLoader │
│ └─ 创建DruidDataSource并注入到DynamicRoutingDataSource │
│ │ │
│ ▼ │
│ 系统就绪 │
│ │
└─────────────────────────────────────────────────────────────────┘
为什么用SmartInitializingSingleton?
问题:@Configuration类会被ConfigurationClassPostProcessor特殊处理
→ CGLIB增强失败 → 影响LiteFlow初始化
解决方案:使用@Component + SmartInitializingSingleton
- 不会被ConfigurationClassPostProcessor特殊处理
- 在所有Bean初始化完成后才执行
- 确保LiteFlow等框架已完全就绪
3.4 数据库表设计
驱动表(jdbc_driver)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INT | 主键 |
| driver_name | VARCHAR | 驱动名称 |
| database_type | VARCHAR | 数据库类型(MYSQL/ORACLE/POSTGRESQL/DM等) |
| driver_version | VARCHAR | 驱动版本号 |
| driver_class | VARCHAR | 驱动类全路径 |
| file_path | VARCHAR | 驱动文件存储路径 |
| file_name | VARCHAR | 原始文件名 |
| file_size | BIGINT | 文件大小 |
| is_builtin | TINYINT | 是否系统内置 |
| is_enabled | TINYINT | 是否启用 |
数据源表(datasource_info)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INT | 主键 |
| name | VARCHAR | 数据源名称 |
| url | VARCHAR | 连接字符串 |
| username | VARCHAR | 用户名 |
| password | VARCHAR | 密码 |
| driver | VARCHAR | 驱动类名 |
| driver_id | INT | 关联的驱动ID |
四、技术栈选型
| 层面 | 技术选型 | 选型理由 |
|---|---|---|
| 后端框架 | Spring Boot 2.7.2 | 成熟稳定,生态完善 |
| 动态数据源 | baomidou dynamic-datasource | 支持多数据源动态切换 |
| 连接池 | Druid | 支持ClassLoader设置,性能优秀 |
| 消息队列 | RabbitMQ | 可靠性高,支持延迟队列 |
| 流程引擎 | LiteFlow | 支持脚本扩展,灵活编排 |
| 前端框架 | Vue 2 + Element UI | 组件丰富,开发效率高 |
五、支持的数据库类型
| 数据库类型 | 驱动类名示例 | 验证SQL |
|---|---|---|
| MySQL | com.mysql.cj.jdbc.Driver | SELECT 1 |
| Oracle | oracle.jdbc.OracleDriver | SELECT 1 FROM DUAL |
| PostgreSQL | org.postgresql.Driver | SELECT 1 |
| SQL Server | com.microsoft.sqlserver.jdbc.SQLServerDriver | SELECT 1 |
| 达梦(DM) | dm.jdbc.driver.DmDriver | SELECT 1 |
| 人大金仓(KingBase) | com.kingbase8.Driver | SELECT 1 |
| 南大通用(GBase) | com.gbase.jdbc.Driver | SELECT 1 |
| 虚谷(XUGU) | com.xugu.cloudjdbc.Driver | SELECT 1 |
六、项目结构
data-synchronism/
├── common/ # 公共模块
│ ├── classloader/ # 动态类加载器
│ ├── config/ # 核心配置(驱动管理、数据源)
│ ├── entity/ # 实体类
│ ├── domain/ # 领域对象
│ └── utils/ # 工具类
│
├── admin/ # 管理后台模块
│ ├── controller/ # REST API
│ └── service/ # 业务逻辑
│
└── dataflow/ # 数据流模块
├── task/ # 定时任务
└── mq/ # 消息队列消费者
admin_web_source_vue/ # 前端源码
├── src/
│ ├── api/ # API接口封装
│ ├── views/ # 页面视图
│ │ ├── jdbc-driver/ # 驱动管理
│ │ ├── data-source/ # 数据源管理
│ │ └── sql-config/ # SQL配置
│ └── components/ # 通用组件
七、总结
本系统通过自定义ClassLoader + DriverShim包装器 + 线程上下文管理三大核心技术,实现了JDBC驱动的运行时热加载。
核心设计思想:
- ClassLoader隔离:每个驱动使用独立的ClassLoader,避免类冲突
- 按需懒加载:启动时不加载驱动,数据源配置时按需加载
- 生命周期管理:支持驱动的加载、卸载、重载
- 初始化时序控制:SmartInitializingSingleton确保框架就绪后再加载数据源
- 线程安全:ConcurrentHashMap缓存,确保多线程环境安全
本文来自博客园,作者:Rolay,转载请注明原文链接:https://www.cnblogs.com/rolayblog/p/21280851

浙公网安备 33010602011771号