Spring Boot 3多数据源配置与Druid监控实践
Spring Boot 3 正式发布以后升级的呼声一直没断过但真正动手的时候很多团队会卡在一个老问题上项目里那套多数据源配置还能不能继续用Druid 的监控页面还能不能顺利打开。我最近把一个业务系统从 Spring Boot 2.7 升到 3.2多数据源这块就折腾了差不多两天期间踩了连接池冲突、事务切库失效、监控 404 这些坑。这篇文章就完整复盘一遍Spring Boot 3 下多数据源配置 Druid 监控从选型思路到依赖接入从 yaml 到注解再到我实际排错的过程一次讲透。这套方案适合谁正在做 Spring Boot 3 升级的人、需要在项目里做读写分离或按业务拆库的人、以及想给连接池接入可视化监控但不知道从哪下手的人。文章里的配置和代码都是我实际跑通过的可以直接复制到项目里改一改就能用。1. 多数据源场景分析与方案选型1.1 什么业务真正需要多数据源先说清楚一个问题不是所有项目都需要多数据源。我见过不少团队明明一个库就能搞定硬是拆了两个数据源结果事务、查询、配置全乱套。真正需要多数据源的场景其实很典型大概就下面这几类。第一类是读写分离。主库负责增删改从库负责查询这是最常见也最合理的使用方式。比如一个内容管理系统用户读文章的频率远高于写文章把查询请求打到只读从库上主库的压力能小很多。但读写分离有个天然的坑主从延迟。刚写入的数据立刻去从库查可能查不到所以这类场景通常只把非实时类的查询放到从库。第二类是业务库拆分。用户模块一个库、订单模块一个库、日志模块一个库微服务架构里很常见。单体应用如果也要关联多个库的数据多数据源就不可避免。第三类是报表与分析类查询。业务库和报表库分开把耗时的统计查询扔到专用的报表库上避免影响线上事务。这类需求通常还伴随只读账号、独立连接池等要求。我自己接手过的系统属于第二类和第三类的混合主业务库是 MySQL还有一个独立的统计库里面跑着各种聚合查询。升级到 Spring Boot 3 之前这套系统用原生 JDBC 写了好几个 DataSource然后靠一个手写的工具类来回切换维护起来相当痛苦。所以这次升级我第一件事就是把多数据源这块重构成一个统一方案。1.2 几种实现方案的取舍多数据源的实现方式我大概梳理了一下主流的有三种。第一种是手写 AbstractRoutingDataSource。这是 Spring 自带的动态路由数据源核心思路是维护一个 targetDataSources 的 Map再通过自定义注解 AOP 在运行时决定当前线程走哪个数据源。优点是没有任何额外依赖纯 Spring 原生能力缺点是代码全部要自己写数据源注册、线程上下文保存、切面逻辑、事务配合全得自己维护。搞个 demo 玩玩可以真要上生产团队成员多起来以后很容易写出各种千奇百怪的路由逻辑。第二种是引入 ShardingSphere。功能非常强不只是多数据源分库分表、读写分离、数据脱敏都能做。但问题也很明显太重。如果项目只需要一个简单的多数据源能力把 ShardingSphere 引进来配置复杂度、运维成本、学习成本都上来了属于杀鸡用牛刀。第三种是 dynamic-datasource-spring-boot-starter。这是 MyBatis 系团队出的一个轻量级多数据源框架核心特性就是 DS 注解切换数据源支持动态配置、分组主从、嵌套事务还能和 MyBatis-Plus 无缝配合。引入成本极低配置量很小对现有代码的侵入也小。我最终选的就是这个。Druid 的选择逻辑也很直接。Spring Boot 3 默认连接池是 HikariCP性能确实好但它的监控能力很弱想拿到 SQL 执行统计还得自己接 Micrometer。Druid 作为阿里开源的连接池除了性能也不错之外自带一套完善的 SQL 监控体系StatFilter 采集 SQL 执行时间、并发、行数等指标StatViewServlet 提供可视化监控页面WebStatFilter 关联 Web 请求与 SQL 执行。对团队来说一个能直接看页面的监控比一堆让人看不懂的指标有意义得多。所以最终的方案就是 dynamic-datasource 管理多数据源底层连接池用 Druid监控页面用 Druid 自带的那套。这套组合在 Spring Boot 2 时代已经很成熟Spring Boot 3 下只要注意几个兼容点同样很稳。2. 升级到 Spring Boot 3 的依赖与环境准备2.1 前置条件JDK 17 与 jakarta 命名空间Spring Boot 3 的第一个硬性要求是 JDK 17。如果你的开发环境还停留在 JDK 8那就得先把 JDK 升上来。这不是简单地改一个版本号项目里用到的第三方库、IDE 设置、Maven 的 compiler 配置都要跟着调整。第二个大的变化是命名空间从 javax 换成了 jakarta。这个改动直接影响所有依赖 Servlet API 的库。以前你写javax.servlet.http.HttpServletRequest在 Spring Boot 3 里必须改成jakarta.servlet.http.HttpServletRequest。如果项目里有些老依赖还硬编码了 javax 的包名启动时就会报ClassNotFoundException或者NoClassDefFoundError。Druid 这块尤其要注意。早期版本的 Druid 用的还是 javax.servletSpring Boot 3 下注册 StatViewServlet 时就会因为找不到类而失败。解决方法是把 Druid 升级到 1.2.20 或更高版本这个版本已经完成了对 jakarta 的适配。我最初没注意这个细节直接用 1.2.8 的老版本结果启动直接报错这就是典型的升级兼容问题。2.2 Maven 依赖如何加而不冲突项目骨架我用的是 spring-boot-starter-parent 3.2.5如果你的项目有自己的父 POM建议也统一用 dependencyManagement 管理 Spring Boot 版本避免多个 starter 之间版本不一致。下面是我实际用下来的依赖清单直接贴出来parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version relativePath/ /parent properties java.version17/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version4.3.0/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.20/version /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version3.5.7/version /dependency /dependencies这里有几个点要特别说明。第一dynamic-datasource 4.3.0 是适配 Spring Boot 3.x 的版本。如果你的项目还在用 Spring Boot 2.x别用 4.x用 3.x 的 dynamic-datasource 更稳。第二Druid 依赖这里我故意只引入了核心包com.alibaba:druid而不是常见的druid-spring-boot-starter。原因后面会讲主要是为了避免两个 starter 的自动配置打架。如果你发现 dynamic-datasource 自己已经传递依赖了某个 Druid 版本也可以不显式声明但我建议还是显式指定让版本可控排查依赖问题时少一些不确定性。第三MyBatis-Plus 在 Spring Boot 3 下要引入mybatis-plus-spring-boot3-starter不是老的那个mybatis-plus-boot-starter。这个非常容易踩依赖名只差一个 3但对应环境完全不同。依赖加好后跑一下mvn dependency:tree重点看看 dynamic-datasource 和 druid 的实际版本以及是否有重复的 druid 依赖。我遇到过 spring-boot-starter-parent 里管理的某个组件和 druid 之间有冲突的情况这时候用mvn dependency:tree -Dverbose -Dincludescom.alibaba:druid就能看到完整的依赖链路定位问题非常快。3. 多数据源核心配置与 DS 使用3.1 一份能直接用的 application.yml依赖配好之后核心就是