
1. 起因为什么 SpringBoot3 里选 Druid而不是 HikariCPSpringBoot 2.x 之后默认的数据库连接池换成了 HikariCP性能确实强但很多老项目从 SpringBoot2 迁到 SpringBoot3 时还是宁愿用 Druid。原因很简单Druid 自带监控、SQL 防火墙、慢 SQL 统计、密码加密这些能力HikariCP 基本都要自己另外写代码。尤其在国内的公司内部很多团队早就把 Druid 监控页当成上线前的必检项换成 HikariCP 意味着要重新做一套监控方案成本太高。我做 SpringBoot3 系列学习笔记这段时间正好有个项目从 SpringBoot 2.7 升级到 SpringBoot 3.2顺带把 Druid 从 1.x 升到最新版踩了一堆坑。这一篇就把 Druid 在 SpringBoot3 里的配置、监控、密码加密、常见坑全部理清楚给正在迁移或者准备用 SpringBoot3 Druid 的朋友一个可以直接抄的作业。先说结论SpringBoot3 用 Druid核心就三件事——引入依赖、配置数据源、开启监控页。但真正让人头疼的是版本兼容、过滤器注册、连接池参数调优以及密码加密那套东西。这篇全展开讲。2. 版本选择这一步错了后面全白搭2.1 SpringBoot3 对 Druid 版本的硬性要求SpringBoot3 底层用了 Jakarta EE 规范包名从javax.*变成了jakarta.*。Druid 如果要适配 SpringBoot3必须用 1.2.20 以上的版本早期 1.2.18 以下的版本还是基于javax.servlet写的直接引入会导致启动报ClassNotFoundException: javax.servlet.Filter。我实测过几个版本Druid 版本SpringBoot3 兼容性说明1.2.19部分兼容连接池能用但内置监控 Filter 注册会异常1.2.20兼容支持jakarta.servlet官方开始适配1.2.21兼容修复了一些统计和密码加密的问题推荐使用所以建议直接上 1.2.2x 最新稳定版。我这次用的是 1.2.21目前网上普遍反馈这个版本在 SpringBoot3.2 下很稳。如果你的项目是 SpringBoot3.0/3.1也建议选这个版本。2.2 引入依赖的正确姿势Druid 有两个依赖别搞混。一个是druid-spring-boot3-starter专为 SpringBoot3 设计另一个是老的druid-spring-boot-starter那是给 SpringBoot2 用的。网上很多教程还停留在老版本直接复制到 SpringBoot3 项目里就会出问题。dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot3-starter/artifactId version1.2.21/version /dependency如果你是 Maven 中央仓库找不到这个 artifact说明仓库没同步或者你本地仓库缓存太旧清一下.m2目录再重新拉就行。Gradle 项目对应写法implementation com.alibaba:druid-spring-boot3-starter:1.2.21这里有一个很多人忽略的点druid-spring-boot3-starter内置了druid核心包和自动配置类不需要再单独引入druid包。再引入一次反而可能产生类冲突比如DruidDataSourceAutoConfigure被加载两次。3. Druid 核心配置详解每个参数到底在调什么3.1 基础连接池配置先看一个标准的application.yml配置我直接贴出我用在项目里的版本spring: datasource: druid: # 数据库连接配置 url: jdbc:mysql://localhost:3306/springboot3_demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver # 初始连接数 initial-size: 5 # 最小空闲连接数 min-idle: 5 # 最大活跃连接数 max-active: 20 # 获取连接最大等待时间单位毫秒 max-wait: 60000 # 间隔多久才进行一次检测检测需要关闭的空闲连接单位毫秒 time-between-eviction-runs-millis: 60000 # 连接保持空闲而不被驱逐的最小时间单位毫秒 min-evictable-idle-time-millis: 300000 # 验证连接是否有效的 SQL validation-query: SELECT 1 # 申请连接时执行 validation-query 检测 test-while-idle: true # 申请连接时检测false 会提升性能 test-on-borrow: false # 归还连接时检测false 会提升性能 test-on-return: false # 是否缓存 preparedStatement pool-prepared-statements: true # 每个连接上缓存的 preparedStatement 数量 max-pool-prepared-statement-per-connection-size: 20 # 配置监控统计拦截的 filters去掉后无法使用监控页 filters: stat,wall,slf4j # 是否开启连接池的 keep-alive keep-alive: true # 物理连接初始化时创建连接数 phy-max-use-count: 1000这些参数中最容易踩坑的是max-wait。我见过不少新人在高并发场景下把max-wait设为 0表示无限等待结果数据库一抖动所有线程全部卡死在拿连接上接口直接超时。建议设置成 5000~6000ms配合下面要讲的remove-abandoned才能真正兜底。3.2 连接池参数背后的原理initial-size不是越大越好。启动时就创建 5 个连接对大多数项目够了。如果你把initial-size改成 50项目启动时会瞬间建立 50 个 MySQL 连接数据库端的连接数、内存、线程全部被打满尤其是多实例部署的场景每个实例启动都来一下数据库很容易卡死。time-between-eviction-runs-millis和min-evictable-idle-time-millis这两个参数要注意配合。前者是检测线程多久跑一次后者是连接闲置多久才被驱逐。我一般设置前者 60 秒、后者 300 秒。如果你的业务对空闲连接很敏感比如连接被防火墙切断了可以把min-evictable-idle-time-millis调小一些但别小于 60 秒否则检测线程频繁驱逐连接反而导致新连接反复创建。keep-alive这个参数很有用。开启之后Druid 会通过validation-query来保活空闲连接防止 MySQL 的wait_timeout把连接断开。MySQL 默认wait_timeout是 8 小时如果你的连接池很少主动检测8 小时后连接其实已经死了但 Druid 不知道下次拿去用就报Communications link failure。开启keep-alive 合理的检测间隔就能解决。3.3 生产环境强烈建议开启的配置除了基础参数生产环境我还会加这几项spring: datasource: druid: # 开启超时连接回收 remove-abandoned: true # 超时时间单位秒 remove-abandoned-timeout: 180 # 关闭 abandoned 连接时输出日志 log-abandoned: true # 物理连接最大使用次数超过后强制重建连接 phy-max-use-count: 1000remove-abandoned是救命的。它会把那些超过remove-abandoned-timeout还没归还的连接暴力回收。比如你代码里忘了finally { conn.close(); }连接会被一直占用不放正常情况下连接池会被耗尽开启这个参数后180 秒没归还就直接断了虽然拿到的连接可能已经不可用但至少不会造成连接池整体瘫痪。phy-max-use-count设置物理连接使用次数超过 1000 次后重建可以避免 MySQL 长时间持有同一个连接导致的报错或者状态残留。4. 监控页配置Druid 最亮眼的功能别浪费了4.1 开启监控统计功能Druid 最吸引人的就是自带的 Web 监控页能展示连接池状态、SQL 执行统计、慢 SQL 日志、Session 监控等功能。在 SpringBoot3 里开启并不复杂但需要知道几个关键点。首先filters配置里要包含stat否则统计功能不生效。我配置的是stat,wall,slf4j解释一下stat开启 SQL 统计监控页能看到每条 SQL 的执行次数、耗时、并发等wall开启 SQL 防火墙拦截危险 SQL比如delete from table_name无 where 条件的操作slf4j把 SQL 执行日志输出到日志系统调试时很有用注意wall过滤器比较严格有些项目用了大量所谓的“动态 SQL”或者特殊语法会被误拦截。这时可以根据你的 SQL 特点拉黑名单或者白名单配置后面我会讲到。4.2 注册 Servlet 和 FilterSpringBoot3 里如果要访问/druid/index.html监控页需要注册一个StatViewServlet拦截/druid/*。同时要注册WebStatFilter用于采集 Web 应用层的请求监控数据。我在项目里单独写了一个配置类这样比在application.yml里配更直观package com.example.demo.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; Configuration public class DruidConfig { /** * 绑定 application.yml 中 spring.datasource.druid 配置到 DruidDataSource */ Bean ConfigurationProperties(prefix spring.datasource.druid) public DataSource druidDataSource() { return new DruidDataSource(); } /** * 注册 Druid 监控页 Servlet */ Bean public ServletRegistrationBeanStatViewServlet druidStatViewServlet() { ServletRegistrationBeanStatViewServlet registrationBean new ServletRegistrationBean(new StatViewServlet(), /druid/*); MapString, String initParams new HashMap(); // 监控页登录账号 initParams.put(loginUsername, admin); // 监控页登录密码 initParams.put(loginPassword, admin123); // IP 白名单留空则允许所有 IP initParams.put(allow, ); // IP 黑名单 initParams.put(deny, 192.168.1.100); registrationBean.setInitParameters(initParams); return registrationBean; } /** * 注册 Web 请求监控过滤器 */ Bean public FilterRegistrationBeanWebStatFilter webStatFilter() { FilterRegistrationBeanWebStatFilter registrationBean new FilterRegistrationBean(); registrationBean.setFilter(new WebStatFilter()); // 拦截所有请求 registrationBean.addUrlPatterns(/*); MapString, String initParams new HashMap(); // 忽略静态资源 initParams.put(exclusions, *.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*); registrationBean.setInitParameters(initParams); return registrationBean; } }这段配置有两个坑要提醒你坑一数据源绑定前缀。如果application.yml里除了spring.datasource.druid还有spring.datasource下的基础配置比如driver-class-name你使用ConfigurationProperties(prefix spring.datasource.druid)时一定要保证url、username、password全部在druid节点下面否则DruidDataSource的属性绑定不到。因为spring.datasource.druid是 Druid 自己的前缀SpringBoot 的DataSourceAutoConfiguration默认读的是spring.datasource二者不能混用。实际上druid-spring-boot3-starter里的自动配置类DruidDataSourceAutoConfigure已经帮你处理了它会识别spring.datasource.druid.*配置并绑定到DruidDataSource上。所以如果走自动配置你不需要再手动写DruidDataSourceBean。如果你写了上面的那个druidDataSource()方法反而容易和自动配置冲突。我习惯保留手动配置因为可以更清晰地控制数据源生命周期但要注意取消自动配置的某些条件。经过实测最稳妥的方式还是只使用druid-spring-boot3-starter的自动配置然后在application.yml里写全spring.datasource.druid配置。因为一旦你自己创建DruidDataSourceBeanSpringBoot 的数据库自动配置会认为你不需要它来初始化数据源可能会影响事务管理器和 JPA/MyBatis 自动配置导致启动报错。坑二allow 参数。allow默认空字符串是全部放行deny是黑名单。如果你设置了allow为具体 IP比如127.0.0.1那局域网里其他机器就无法访问监控页了。我在公司环境里是留空然后利用 Nginx 做内网访问控制这样更灵活。4.3 访问监控页配置完成后启动项目浏览器访问http://localhost:8080/druid/index.html输入你配置的账号密码就能看到监控页了。里面能看数据源当前连接数、活跃连接数、空闲连接数、等待次数、错误次数SQL 监控每条 SQL 的执行次数、平均耗时、最大耗时、错误次数SQL 防火墙被拦截的 SQL 记录Web 应用请求 URL、请求次数、耗时Session 监控当前 Session 列表这些数据对排查性能问题特别有用。我在一次压测中发现有一条 SQL 平均耗时 800ms通过监控页定位到 SQL加上索引后直接把接口响应从 1.2 秒降到 200ms。5. 数据库密码加密祛魅与实操5.1 为什么要加密application.yml里明文写数据库密码是很多公司安全审计的红线。Druid 支持对密码做 RSA 加密后再配置启动时自动解密这样即使配置文件泄露别人拿到的也是密文。网上关于这块的教程很多但有不少人只讲了加密步骤没说清楚原理导致看着密文心里没底。这里我把原理拆开讲透。5.2 加密原理与操作步骤Druid 的密码加密其实用了 RSA 非对称加密。它生成一对密钥私钥用于解密公钥用于加密。在生成密文时使用公钥对明文密码加密得到密文在运行时Druid 通过配置里的私钥解密得到明文密码。具体操作分四步第一步生成密文。使用 Druid 的ConfigTools类生成密文和私钥、公钥。最简单的做法是通过 Maven 执行mvn com.alibaba:druid:1.2.21:tools或者直接运行java -cp druid-1.2.21.jar com.alibaba.druid.filter.config.ConfigTools yourpassword屏幕上会输出privateKey、publicKey、password其中password就是加密后的密文privateKey是解密时用的publicKey是理论上给别人加密时用的。第二步修改配置。把原来明文密码替换成密文同时新增publickey配置项并开启connection-properties里的解密选项spring: datasource: druid: username: root password: QmYz4...上面生成的密文 # 开启数据库密码解密 filters: config,stat,wall,slf4j connection-properties: druid.stat.slowSqlMillis5000;config.decrypttrue;config.decrypt.key${DRUID_PRIVATE_KEY}这里有两个关键点filters里必须加上config否则解密过滤器不生效。config.decrypt.key是私钥。私钥不能直接暴露在application.yml里否则密文形同虚设。正确做法是通过环境变量注入。第三步环境变量管理私钥。在启动项目时传入环境变量java -jar demo.jar --DRUID_PRIVATE_KEY你的私钥或者更规范一点在服务器上配置环境变量export DRUID_PRIVATE_KEY你的私钥然后application.yml里用 placeholder 引用connection-properties: druid.stat.slowSqlMillis5000;config.decrypttrue;config.decrypt.key${DRUID_PRIVATE_KEY}这样即使配置文件泄露黑客拿不到环境变量也解不开密码。第四步验证效果。启动项目看日志是否报druid decrypt fail。如果报错多半是私钥换行、空格或编码问题。私钥是很长的一串 Base64 字符复制粘贴时不要带换行不要在前后加空格。5.3 若依框架集成的参考最近很火的若依框架RuoYi在它的代码生成模块里也集成了 Druid 密码加密。我特意去翻过源码它的做法是把私钥配置在druid.yml里然后通过ConfigurationProperties读取。如果你用的是若依直接在application-druid.yml里找到password和publicKey改成你自己的即可。不过有一点要提醒若依框架默认没有开启config过滤器需要在filters中加上config否则解密不会执行。这也是很多人在若依里配了加密但启动报密码错误的原因。6. SpringBoot3 Druid 实战整合 MyBatis 并调通监控6.1 项目依赖一览这里我放一个完整的pom.xml依赖清单方便大家照搬dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version3.0.3/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot3-starter/artifactId version1.2.21/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency /dependencies如果你的项目用的是 MyBatis-Plus把mybatis-spring-boot-starter换成mybatis-plus-boot-starter版本用最新的 3.5.x 系列兼容 SpringBoot3。6.2 完整的 application.yml 配置我给出一个整合了监控、防火墙、慢 SQL 日志、密码加密的生产级配置spring: application: name: druid-demo datasource: druid: url: jdbc:mysql://192.168.1.10:3306/demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: ${DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 keep-alive: true phy-max-use-count: 1000 filters: stat,wall,slf4j connection-properties: druid.stat.slowSqlMillis3000 # Web 监控统计 web-stat-filter: enabled: true url-pattern: /* exclusions: *.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/* # 监控页配置 stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: admin123 allow: deny: 这里我用了环境变量${DB_PASSWORD}而不是明文。如果你的应用部署在 Kubernetes 里可以将数据库密码放到 Secret 中然后把环境变量挂载进 Pod这样配置文件和密钥完全分离。6.3 防火墙常见误拦截与处理wall过滤器默认会拦截很多 SQL。我遇到过一些被误伤的典型场景使用select *且没有 WHERE 条件的大表查询默认会提示“sql not allow”并被记录包含关键字sleep()、benchmark()的函数调用使用LIMIT子句但有语法变体的查询MyBatis 动态 SQL 拼出来的含有concat、ifnull等函数的复杂 SQL被拦截后在监控页的“SQL 防火墙”里能看到详细的拦截原因。解决办法有三种对单个 SQL 不拦截在防火墙配置里配置白名单 SQL降低拦截规则修改wall过滤器的config参数直接去掉wall过滤器不推荐生产环境我一般用一种折中方案在application.yml里单独配置wall的拦截策略druid: filter: wall: enabled: true config: delete-allow: false drop-table-allow: false alter-table-allow: false set-allow: false这样只禁止最危险的 DDL 和无 WHERE 的 DELETE其他 SQL 放行生产环境更实用。6.4 慢 SQL 日志配置connection-properties里设置了druid.stat.slowSqlMillis3000表示超过 3000ms 的 SQL 会被记录。配合slf4j过滤器慢 SQL 会输出到日志中。我建议在项目里也配置 MyBatis 的慢 SQL 日志mybatis: configuration: log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl再配合 logback 的日志级别设置 mapper 包下的日志为 debug就能看到 SQL 执行详情。7. 常见问题与排查技巧实录7.1 启动时报ClassNotFoundException: javax.servlet.Filter这是最经典的 SpringBoot3 迁移问题。原因是你的 Druid 版本低于 1.2.20或者引错了依赖。解决方式就是换成druid-spring-boot3-starter1.2.2x。如果你的项目还有其他框架引入了老实现可以通过排除依赖来隔离dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot3-starter/artifactId version1.2.21/version exclusions exclusion groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId /exclusion /exclusions /dependency7.2 监控页打开白屏或 404监控页 404 大多是因为StatViewServlet没注册成功。检查三件事pom.xml里是否引入了spring-boot-starter-webDruid 的 Web 监控需要 Servlet 容器支持stat-view-servlet.enabled是否为true默认可能关闭项目是否被某个全局过滤器拦截了/druid/*请求另外注意如果你的项目使用了 Spring Security需要把/druid/**加入白名单否则会被权限拦截表现为“无权限访问”。7.3 数据源首次请求报连接错误排除密码错误后最常见的两个原因allowPublicKeyRetrievaltrue没加。MySQL 8 默认用 caching_sha2_password 认证连接时如果不允许公钥检索会报Public Key Retrieval is not allowed。serverTimezone没设置。没设置时Druid 和 MySQL 之间时区不匹配会报serverTimezone异常。我这里的配置用的Asia/Shanghai。7.4 密码加密后启动失败报错信息一般是oracle.net.ns.NetException: Io exception或者Caused by: java.lang.IllegalStateException: druid decrypt fail。排查顺序filters里有没有加config过滤器connection-properties里的config.decrypt.key是否引用的环境变量如果直接在 yml 里写私钥且私钥有换行会解析失败私钥值是否有空格Base64 里不要有换行符7.5 采用表格形式汇总排查清单问题现象可能原因快速排查命令/工具监控页 404StatViewServlet 未注册访问/druid/login.html试试如果也 404说明没注册连接超时max-wait 太小 / 数据库连接数耗尽查看监控页“数据源”页签里的等待次数SQL 被拦截wall 防火墙规则查看监控页“SQL 防火墙”标签页连接耗尽连接未释放 / remove-abandoned 未开启监控页显示PoolableConnection数量始终为 max-active启动慢initial-size 过大看启动日志中创建连接耗时无法访问数据库MySQL 8 认证问题连接串加allowPublicKeyRetrievaltrueuseSSLfalse8. 关于参数调优的个人经验Druid 参数大都是参考值最终还是要看压测结果。我分享一套我自己常用的调优思路不一定适合所有项目但可以给大家一个起点。如果业务是读多写少的简单查询单机并发五百以内max-active建议 20 就够initial-size5min-idle5。这种情况下即使某个慢 SQL 拖慢了数据库连接池也不会瞬间被打爆。如果业务有批处理任务比如定时跑几个小时的报表任务需要一次性拿大量连接可以把max-active调到 50同时调大max-wait到 30000ms。但要注意数据库端max_connections的限制一般 MySQL 默认 151你连接池 50 个加上其他应用和运维工具很容易打满。这时反而应该给数据库也调大max_connections或者减少连接池数量。还有一个小窍门把time-between-eviction-runs-millis和min-evictable-idle-time-millis的值错开一些比如前者 60 秒、后者 300 秒。这样检测线程至少每 60 秒跑一次但空闲连接要等 300 秒才会被清理避免连接频繁创建销毁。连接创建开销很大尽量保持池子里有常用连接。9. 写在后面的小建议这一篇从版本选择、配置参数、监控页到密码加密、常见问题基本把 SpringBoot3 集成 Druid 的主要环节都过了一遍。最后给新人一个建议不要上来就直接复制一大段配置而是先按照官方文档的最小配置跑通再逐步加监控、防火墙、加密这些功能。每次只加一个功能出了问题时你能快速定位是哪个配置引起的。我用这方案在本周刚把一个生产项目从 SpringBoot2.7 升级到 SpringBoot3.2从引入依赖到监控页跑通整个过程花了不到半小时。踩过的坑主要是版本其次是防火墙误拦截。按这篇文章的配置走你应该能更顺畅。如果你在集成中遇到其他怪问题欢迎在评论区把报错日志贴出来我帮你看看到底是哪一步没到位。