Python 实战:构建支持模块热插拔的本地 WebSocket 实时日志面板

在复杂的企业系统中,经常会遇到以下场景:

  • 一个系统连接多个业务数据库(如订单库、用户库、商品库)

  • 实现主从数据库的读写分离

  • 多租户 SaaS 系统中,每个租户一个库

如果继续使用默认的 @Primary + @Qualifier 方式手动注入,会导致代码复杂,耦合严重。为此,构建一个“ 可自动识别上下文、支持注解方式的多数据源切换机制”就显得尤为重要。


一、核心思路概览

  • 使用 AbstractRoutingDataSource 动态决定当前数据库连接

  • 定义 ThreadLocal 作为数据源上下文

  • 通过自定义注解 @DS("order") 动态切换数据源

  • AOP 拦截注解并切换上下文


二、数据库准备

准备两个数据库(可用同一 MySQL 服务下的不同库):

  • db_user:保存用户信息

  • db_order:保存订单数据

初始化建表 SQL 示例:

sql复制编辑CREATE DATABASE db_user;CREATE DATABASE db_order;-- 用户表CREATE TABLE user_info (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(100)
);-- 订单表CREATE TABLE order_info (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id BIGINT,
  product VARCHAR(100)
);

三、POM 依赖引入

xml复制编辑      org.springframework.boot    spring-boot-starter-jdbc        mysql    mysql-connector-j        org.springframework.boot    spring-boot-starter-aop  

四、配置多数据源

4.1 application.yml 配置

yaml复制编辑spring:
  datasource:
    user:
      url: jdbc:mysql://localhost:3306/db_user
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    order:
      url: jdbc:mysql://localhost:3306/db_order
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

4.2 创建枚举标识

java复制编辑public enum DataSourceType {
    USER, ORDER
}

4.3 数据源上下文工具类

java复制编辑public class DynamicDataSourceContextHolder {    private static final ThreadLocal contextHolder = new ThreadLocal<>();    public static void set(String dsKey) {
        contextHolder.set(dsKey);
    }    public static String get() {        return contextHolder.get();
    }    public static void clear() {
        contextHolder.remove();
    }
}

五、实现动态数据源路由

java复制编辑public class DynamicRoutingDataSource extends AbstractRoutingDataSource {    @Override
    protected Object determineCurrentLookupKey() {        return DynamicDataSourceContextHolder.get();
    }
}

六、数据源配置类

java复制编辑@Configurationpublic class DataSourceConfig {    @Bean
    @Primary
    public DataSource dataSource(@Qualifier("userDataSource") DataSource userDs,                                 @Qualifier("orderDataSource") DataSource orderDs) {
        Map map = new HashMap<>();
        map.put("USER", userDs);
        map.put("ORDER", orderDs);        DynamicRoutingDataSource ds = new DynamicRoutingDataSource();
        ds.setTargetDataSources(map);
        ds.setDefaultTargetDataSource(userDs);        return ds;
    }    @Bean("userDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.user")
    public DataSource userDataSource() {        return DataSourceBuilder.create().build();
    }    @Bean("orderDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DataSource orderDataSource() {        return DataSourceBuilder.create().build();
    }
}

七、自定义注解实现切换

7.1 定义注解 @DS

java复制编辑@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface DS {
    String value();
}

7.2 AOP 实现注解拦截

java复制编辑@Aspect@Componentpublic class DynamicDataSourceAspect {    @Pointcut("@annotation(ds)")
    public void dsPointcut(DS ds) {}    @Around("dsPointcut(ds)")
    public Object around(ProceedingJoinPoint point, DS ds) throws Throwable {        try {
            DynamicDataSourceContextHolder.set(ds.value().toUpperCase());            return point.proceed();
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
    }
}

八、DAO 与 Service 层调用示例

8.1 Repository 示例(使用 JdbcTemplate)

java复制编辑@Repositorypublic class UserDao {    @Autowired
    private JdbcTemplate jdbcTemplate;    public List> findAllUsers() {        return jdbcTemplate.queryForList("SELECT * FROM user_info");
    }
}
java复制编辑@Repositorypublic class OrderDao {    @Autowired
    private JdbcTemplate jdbcTemplate;    public List> findAllOrders() {        return jdbcTemplate.queryForList("SELECT * FROM order_info");
    }
}

8.2 Service 示例

java复制编辑@Servicepublic class DataService {    @Autowired private UserDao userDao;    @Autowired private OrderDao orderDao;    @DS("user")
    public List> queryUsers() {        return userDao.findAllUsers();
    }    @DS("order")
    public List> queryOrders() {        return orderDao.findAllOrders();
    }
}

九、测试 Controller

java复制编辑@RestController@RequestMapping("/api")public class TestController {    @Autowired
    private DataService dataService;    @GetMapping("/users")
    public Result getUsers() {        return Result.success(dataService.queryUsers());
    }    @GetMapping("/orders")
    public Result getOrders() {        return Result.success(dataService.queryOrders());
    }
}

十、扩展建议

场景 解决方案
多租户动态数据库连接 结合租户 ID + 连接池缓存机制,实现动态构建 DataSource
多模块共享数据源配置 使用 spring.factories 统一注册配置
Mapper 层集成 MyBatis 支持 SQLSessionFactory 动态注册
读写分离场景支持 配合 AOP 或 SQL 注解区分读写策略
使用 Spring Cloud + Nacos 多数据源配置可从 Nacos 动态拉取

十一、总结

本文完整实现了:

  • 支持多数据源的配置与初始化

  • 自定义注解 + AOP 实现动态切换

  • 使用 ThreadLocal 管理上下文数据源

  • Service 层零侵入式调用多个数据库

  • 可扩展到多租户、读写分离等高级场景

这是中大型系统中常见但复杂度较高的一种实践,通过合理抽象可以极大提升系统的灵活性与扩展性。

相关阅读 资料出处 资料来源 内容参考 延伸阅读 推荐链接 相关信息 参考内容 推荐资料 详细内容 更多内容 查看详情 点击查看 深入了解 官方资料 资料链接 信息来源 阅读更多 文献出处 内容链接 数据出处 技术参考 案例来源 实证依据 报告链接 研究链接 统计数据 数据来源 分析资料 理论出处 论文参考 说明详情 调研内容 支持数据 背景文献 学术出处 报告来源 文献引用 权威来源 实测数据 延伸信息 详情参考 原文链接 看原文 阅读原文 查看原帖 原帖地址 原始出处 相关原文 官方信息 经验来源 更多解析 看更多 网页资料 实用信息 入门参考 指南文档 推荐文章 实例出处 小贴士 快速了解 点此查看 点击跳转 原始链接 网页参考 本文依据 内容跳转 参考文档 跳转详情 网页原文 链接详情 点击去看 点我查看 来源说明 资讯入口 阅读跳转 入门来源 学习资料 背景说明 研究出处 信息拓展 衍生阅读 延展资料 链接来源 原始内容 探索原文 获取信息 查阅资料 获取来源 扩展阅读 查阅详情 来源详情 页面跳转 浏览原文 详情链接 说明出处 内容补充 参考原址 附加链接 来源页面 点此了解更多 原文阅读入口 原始资料入口 深度解析参考 外部资源链接 了解背景内容 延伸阅读入口 本文数据支持 本文相关页面 点这里查看 快速跳转 阅读此文 查看推荐 跳转页面 继续阅读 跳转参考文档 外链参考 去看看 内容指引 访问链接 小贴士链接 链接入口 教程连接 指向原文 本文链接 文献资料 推荐参考 同类资料 引用内容 来源参考 实际出处 文档入口 学习入口 阅读指引 参考原文 拓展来源 额外参考 内容引申 来源位置 附加内容 网页跳转 阅读地址 档案出处 数据支持 引用来源 上游资料 查阅信息 原页链接 推荐说明 提及内容 本文参考 链接参阅 查阅出处 内容输出 内容路径 深度阅读 网文参考 浏览入口 原始输入 本页推荐 外链详情 资讯路径 源文链接 关键参考 相关文章 指定链接 点击链接 原始地址 学术链接 网络出处 案例出处 支撑数据 基础资料 原始信息 详细出处 深入资料 网页内容 推荐原文 网页参考资料 来源跳转 更多指引 查阅页 文本出处 连接参考 说明连接 实用推荐 点开阅读 获取原始资料 快速阅读链接 内容引用来源 实际案例参考 对应来源 跳转路径 路径参考 实际数据链接 原页跳转 外链地址 跳转原始网页 档案链接 本站来源 知识链接 内容出自 信息详情 数据文献 原本出处 权威证据 参考入口 档案信息 联网资料 来源位置说明 推荐文档 详见资料 继续查阅 详情页 资料页面 原始说明 原站内容 内容追踪 访问原页 实例内容 原文传送 权威解读 来自网页 提及出处 网页导航 相关报道 信息补充 数据查看 更多信息点 资料一览 内容载体 报道来源 说明原文 知识参考 网页地址 点我阅读 查阅指引 点此前往 看更多细节 原始数据 跳转资源 文献详情 外部详情 更多原文 入口链接 数据详解 文献链接 学习资源 内容导览 查看原站 链接出自 进一步了解 继续探索 相关背景 外部文档 学习入口链接 原始页面 深层链接 扩展查阅 内部跳转 链接点击 查阅报告 实时信息 全文详情 原始说明页 来源入口 内容浏览 更多指向 外链入口 扫码进入 专题链接 实用出处 历史内容 扩展数据 来源网页 原页入口 快捷入口 相关跳转 原数据页 学术入口 原内容页 文档资源 直接参考 内容起点 资源说明 原链接地址 通用资源 链接访问 推荐入口 跳转原页 引导信息 快速查阅 可用资料 源地址 网站跳转 官方出品 联网跳转 阅读指令 追踪链接 深度链接 阅读页面 外链出自 资料指南 路径链接 入口信息 建议阅读 实际引用 外部入口 阅读快线 页面地址 专业出处 相关资源 网页跳转口 引导路径 来源文档 内容延展 快速入口 了解路径 传送门 详情输入 转跳信息 入口资料 查阅连接 支持出处 深度出处 内容通道 地址传送 页面查看 权威指向 教程出处 精选内容 网页引导 网页原始 平台跳转 网页资源 信息连接 原始下载 资料端口 来源查阅 内容窗口 原文导航 专栏参考 链接地址 信息链接 浏览通道 原始说明链接 外链展示 页面详情 核心来源 内容支持源 指南页 原页资料 深入页面 获取资料 源头链接 栏目内容 进入页面 访问信息 参阅链接 链接引导 全文入口 进入内容页 跳转通道 查阅平台 内容访问 网络入口 页面支撑 推荐查阅 内容页面 外部访问 进入链接页 上游引用 信息导航 文献访问 链接按钮 访问原网页 访问入口 外链原文 页面资源 平台资源 深层资源 资源地址 扩展链接入口 浏览更多内容 推荐通道 可查文献 入口查看 相关补充 数据入口 外链跳转 链接快速通道 查阅原数据 跳转按钮 信息通道 外链页 文章参考链接 页内内容 链接地址参考 推荐信息链接 学术内容入口 原数据链接 内容跳转入口 参考页码 链接文档页 学术查阅页 链接路径参考 外链源页 页面源数据 推荐资源页 网页延伸 详细跳转 官方信息页 内容导向 跳转数据页 信息参考页 内容传送 外链目标页 链接访问页 指向内容页 官方支持页 页面说明来源 访问资源页 延展查阅入口 入口导航页 扩展网页链接 内容资料页 推荐内容页 页面快速跳转 官方页面入口 链接来源页 更多详情 更多参考内容 详细访问链接 页内原文链接 核心阅读链接 快速导航入口 页面跳转入口 研究详情页 查阅详细内容 权威跳转入口 官方参考路径 页面入口说明 内容列表入口 引导页面链接 实用资源页面 数据说明页面 源数据查看 学术路径指引 推荐查阅路径 内容支持页面 指向入口资源 延伸路径指引 详细阅读页 外链访问入口 来源资料页面 内容详情传送 入口链接说明 链接说明页 官方推荐路径 路径延伸阅读 查阅文档页 引导内容入口 核心入口链接 内容补充页面 查阅支持页 跳转详细信息 文献路径引导 信息阅读页面 查看内容页 链接传送入口 外部参考页 相关参考页 文档查看入口 阅读原始页面 页面资源入口 内容平台页 跳转入口页 推荐入口页 来源文献页 官方入口链接 指引查阅路径 页内链接展示 内容平台入口 查阅资源页 链接跳转指引 页面地址入口 权威查阅页面 页面内容参考 原始文献页 深度内容入口 数据资料页面 原始资源页 页面补充信息 实际网页地址 学术查阅入口 平台信息入口 原始信息路径 页面参考来源 实际链接地址 链接入口参考 参考文档页面 外链说明入口 链接直达 深入查证 原文详解 相关页面 来源路径 页面跳出 数据外链 引导阅读 深入来源 原文入口 实际地址 原出处链接 内容源头 查阅通道 额外信息 资料端点 推荐页码 引用文献 内容查询 实时查阅 官方通道 直达资源 导航链接 延伸访问 参考点击 页面查阅 数据访问 平台原始 快速查看 内容接入 跳转查看 页面通道 可用页面 点击参考 文章外链 内容路由 来源查找 路径指引 全部内容 出处文献 文章路径 外链调用 站外链接 路径浏览 外部文献 链接指南 链接点击入口 深入文档 出处路径 学术页面 说明路径 页面文献 网络文档 文档查看页 跳转参考 页面导航链接 关联链接 查看文章 引导说明 数据页入口 原始文档跳转 链接浏览入口 来源阅读页 内容引导页 推荐跳转 可查页面 官方引导 跳转原数据 可跳转文档 平台入口页 网站原始链接 核心信息页 原始站点 延展入口 页面外链 内容展开 更多内容查阅 实际链接入口 文章出处路径 内容传导 快速定位 跳转入口链接 页面入口跳转 内容原始页 文献来源页 外链网页 相关跳转链接 文章链接地址 阅读原页 来源详细信息 官方文档页 页面说明链接 深度查阅链接 页面跳转信息 文档信息源 链接信息入口 网页引导路径 页面导向链接 查阅起点 网页资源链接 指向阅读页 快捷阅读 外部链接跳转 页面访问点 官方链接源 查阅推荐页 路径地址查看 跳转资源入口 查看详细页 跳转信息链接 平台说明页 外部页面访问 阅读页地址 链接页入口 查阅内容来源 内容详细页 原始链接入口 页面内容源 查阅网页链接 可读内容页 文章说明链接 内容浏览器入口 外链浏览页 网页推荐链接 指向页面地址 链接页面详情 页面查阅通道 查阅信息源 平台内容跳转 页面说明文本 可阅读原文 页面指引链接 跳转外链入口 数据推荐页 文章引导页 网页说明内容 浏览内容入口 内容出处导航 信息原文页 页面信息资源 文档源头路径 文章原页跳转 网页地址链接 页面参考资源 内容跳出页 链接定位路径 查阅内容平台 引导参考页面 页面资源查询 内容导览页 链接内容地址 页面导向信息 内容站外入口 阅读文章页 页面平台入口 外链跳转通道 页面参考入口 入口跳转页 引导文献页 数据查阅页 页面通道入口 可跳转平台 实时外链路径 链接外部资源 页面起始链接 参考网页内容 网页内容跳转 文章延伸页 内容查找入口 引导跳转信息 跳转资源页 站外资源查看 资料原始链接 文献通道入口 推荐访问链接 文章外跳入口 页面数据通道 可跳转信息页 路径入口说明 页面源文档 数据延伸链接 页面原路径 引用说明入口 跳转路径源 页面说明内容 页面通行地址 数据查阅地址 可参考原页 页面说明文章 内容路径推荐 跳转资源页面 引导页面信息 页面参考导向 入口跳转说明 文档浏览地址 页面跳转说明 页面内容详情 内容外部路径 网页原文说明 内容查找页 平台外链资源 文献平台入口 路径导航链接 页面跳出内容 外链导向页 网页导航资源 原始资料页面 信息页面链接 内容导向路径 页面指引地址 路径查看入口 内容查阅地址 页面推荐资源 网页地址跳转 原始入口说明 路径说明页面 入口信息地址 页面通道资源 可跳转文献 实用页面链接 页面内容引导 页面引导跳转 跳转页面推荐 参考链接导航 页面外链内容 可阅读内容页 页面推荐信息 页面延伸内容 内容站外链接 页面站外跳转 页面参考信息 页面外跳路径 文档跳转说明 网页推荐跳转 原始资料路径 内容路径跳转 页面内容通道 路径内容浏览 页面内容起点 页面说明路径 文章平台入口 页面数据查阅 页面数据来源 文档通道 路径文章 了解详情页 访问详情 资料跳转页 了解入口 资源数据 链接原文 访问原文 原始通道 内容地址 页面内容 引用资料 阅读路径 跳转入口 说明平台 原文页面 数据说明 内容入口 资料源头 平台通道 路径平台 信息源头 页面导向 说明页面 原文内容 跳转内容 地址详情 资源详情 入口参考 浏览页面 源地址页 页面参考 引导详情 资料平台 内容原始 引用路径 阅读详情 入口路径 通道信息 说明资源 地址内容 引导入口 链接资料 入口文档 访问资源 数据页面 地址资料 文档参考 通道资源 了解数据 路径数据 信息参考 导向页面 源平台页 参考资源 内容引导 页面文档 资料内容 内容数据 了解页面 平台路径 页面入口 路径入口 地址入口 链接说明 引用信息 页面说明 相关阅读 资料出处 资料来源 内容参考

请使用浏览器的分享功能分享到微信等