本文还有配套的精品资源,点击获取
简介:Druid是阿里巴巴开源的高性能数据库连接池,集成了SQL解析、监控统计和安全防护等强大功能,广泛应用于Java企业级开发中。 druid-spring-boot-starter 作为Spring Boot的自动化配置启动器,极大简化了Druid的集成流程。本Demo基于Spring Boot 2.x构建,涵盖Druid的核心配置,包括数据源设置、监控页面启用、Web过滤器与SQL拦截器配置,并提供完整的测试代码验证连接池功能。通过该可运行实例,开发者可快速掌握Druid在实际项目中的应用方法,提升数据库访问性能与系统可观测性。
你有没有遇到过这样的情况:线上服务突然变慢,监控里一堆SQL执行时间飙升,排查半天发现是个没人维护的老接口在疯狂查全表?😅 或者更糟心的是,某天凌晨被叫醒,说数据库被打满了,最后追查到是一段带 UNION SELECT 的恶意请求通过了前端校验……😱
这些问题背后,往往不只是代码的问题,更是 数据访问层治理体系缺失 的体现。而Druid,这个我们天天用却可能“日用而不知”的连接池组件,其实早已为我们准备好了整套解决方案——它不只管连接复用,更是一个集 监控、防护、治理 于一体的数据库网关。
今天,我们就来彻底拆开Druid这头“老黄牛”,看看它是如何从一个简单的连接池,演变成现代Java应用不可或缺的“数据库守门人”的。🛠️
提起Druid,很多人的第一反应是:“哦,那个带监控页面的连接池。”没错,它的StatViewServlet确实让人眼前一亮,但如果你只把它当做一个可视化工具,那就太低估它了。
真正的Druid,是一个 基于标准JDBC扩展机制构建的企业级数据访问治理平台 。它的核心思想是: 把对数据库的每一次交互都纳入管控范围 。
怎么做到的呢?关键就在于它巧妙地运用了两个设计模式:
DruidDataSource 作为连接工厂,统一创建和管理物理连接; Connection 、 Statement 、 ResultSet 进行层层代理,在不侵入业务代码的前提下,实现SQL执行的拦截与增强。 这种架构让它能轻松实现:
✅ SQL执行耗时统计
✅ 慢SQL自动捕获
✅ 连接泄露检测与报警
✅ SQL注入主动防御(wall-filter)
✅ 多维度监控指标暴露
换句话说,Druid让原本“黑盒”的JDBC调用过程变得完全透明,这才是它最强大的地方。👏
现在几乎没人会手动配置Druid了,大家都是靠 druid-spring-boot-starter 一把梭哈。但这背后到底发生了什么?我们真的了解这个Starter是怎么工作的吗?
首先,别小看这一行依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.20</version>
</dependency>
版本选错了,轻则功能缺失,重则启动报错。这里有个经验法则:
尤其是Boot 3之后全面转向Jakarta EE,包名都变了,老版本根本跑不起来。
另外,很多人忽略了一个坑: HikariCP的默认优先级问题 。Spring Boot默认用HikariCP,如果你只加了Druid依赖,但没排除Hikari,那恭喜你,Druid的自动配置会被跳过,因为 DataSourceAutoConfiguration 先执行了!
解决办法很简单,在JDBC starter里排除掉它:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
这样,Druid才能真正“上位”。
一切的核心,就在 DruidDataSourceAutoConfigure 这个类里。我们来扒一扒它的源码:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DruidDataSource.class)
@EnableConfigurationProperties(DruidDataSourceProperties.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class DruidDataSourceAutoConfigure {
@Bean
@Primary
@ConditionalOnMissingBean
public DataSource dataSource(DruidDataSourceProperties properties) {
DruidDataSource dataSource = new DruidDataSource();
properties.configure(dataSource);
return dataSource;
}
}
这几行注解信息量巨大:
@ConditionalOnClass(DruidDataSource.class) :有这个类才加载,避免空指针。 @EnableConfigurationProperties :把YAML里的 spring.datasource.druid.* 自动绑定到POJO。 @AutoConfigureBefore(DataSourceAutoConfiguration.class) :这是最关键的!确保Druid比Hikari先初始化,抢占主数据源位置。 @Primary + @ConditionalOnMissingBean :只注册一次,防止重复定义。 整个流程可以用一张图概括:
flowchart TD
A[启动Spring Boot应用] --> B{类路径是否存在 DruidDataSource?}
B -- 是 --> C[加载 DruidDataSourceAutoConfigure]
B -- 否 --> D[跳过Druid配置]
C --> E[读取 spring.datasource.druid.* 配置]
E --> F[实例化 DruidDataSourceProperties]
F --> G[创建 DruidDataSource Bean]
G --> H{是否启用 StatViewServlet?}
H -- 是 --> I[注册 StatViewServlet 到 Servlet 容器]
H -- 否 --> J[跳过监控页面]
G --> K{是否启用 WebStatFilter?}
K -- 是 --> L[注册 WebStatFilter 拦截请求]
K -- 否 --> M[跳过SQL监控采集]
G --> N[数据源就绪, 应用启动完成]
看到了吗?整个过程完全是“按需加载”,没有一行多余代码。这才是优秀框架的设计哲学。
都说“配置决定命运”,Druid的表现好不好,全看你怎么配。下面我们来聊聊几个关键参数的最佳实践。
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
username: devuser
password: ENC(xxx) # 别再明文写密码了!
driver-class-name: com.mysql.cj.jdbc.Driver
几个细节要注意:
serverTimezone=Asia/Shanghai :不设这个,你的 LocalDateTime 很可能存进去就偏移了8小时。 characterEncoding=UTF-8 :中文乱码的罪魁祸首之一。 jasypt-spring-boot-starter ,实现运行时解密,安全又优雅。 至于 driver-class-name ,MySQL 8+其实可以省略,Spring Boot能自动推断。但在Docker或K8s环境下建议显式指定,以防SPI机制失效。
spring:
datasource:
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 1800000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
这套配置堪称“黄金组合”,我们逐个拆解:
initial-size 和 min-idle :告别首请求慢 这两个值通常设成一样,比如5~10。作用是在应用启动时就建立一批连接,避免第一个用户请求卡住去建连。
小贴士:设置太高会导致启动慢,一般不超过20。
Druid提供了四种检测方式:
test-on-borrow test-on-return test-while-idle validation-query 后台有一个Eviction线程,每隔 time-between-eviction-runs-millis 秒扫描一次,只对空闲超过 min-evictable-idle-time-millis 的连接执行 SELECT 1 测试。既保证了连接活性,又不会频繁打扰数据库。
💡 经验公式:
minEvictableIdleTimeMillis ≤ wait_timeout × 0.9,防止数据库先断开。
单数据源只是起点,真正的挑战在于复杂场景下的灵活控制。
当你有两个数据库时,必须明确告诉Spring哪个是主数据源:
@Bean("primaryDataSource")
@Primary
@ConfigurationProperties("spring.datasource.druid.primary")
public DataSource primaryDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean("secondaryDataSource")
@ConfigurationProperties("spring.datasource.druid.secondary")
public DataSource secondaryDataSource() {
return DruidDataSourceBuilder.create().build();
}
加上 @Primary ,Spring才会优先注入它。否则, @Autowired DataSource 就会抛出 NoUniqueBeanDefinitionException 。
MyBatis也得跟着配:
@Bean
public SqlSessionFactory primarySqlSessionFactory(
@Qualifier("primaryDataSource") DataSource ds) throws Exception
这样才能精准路由到对应的库。
即使设置了 initial-size ,也不能保证所有连接都ready。更稳妥的做法是启动后主动“暖一下”:
@Component
public class DataSourceWarmer implements ApplicationListener<ApplicationReadyEvent> {
@Autowired
private DataSource dataSource;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
warmUpDataSource();
}
private void warmUpDataSource() catch (SQLException e) {
log.warn("⚠️ Failed to warm up DataSource: {}", e.getMessage());
}
}
}
监听 ApplicationReadyEvent ,等所有Bean都准备好后再执行一次简单查询,确保连接池已激活。这对高并发系统上线至关重要,能有效降低P99延迟波动。
你以为 maxActive=20 就是随便写的?Too young. 实际上,合理的连接数应该基于QPS和平均响应时间来计算。
来看这三个关键参数:
initialSize minIdle maxActive 它们的关系就像水池的进出水管:
initialSize 是初始水位; minIdle 是最低水位警戒线,低于此值会自动补水; maxActive 是最高容量,超过就得排队( maxWait )。 所以,一个健康的连接池应该是: 平时维持一定空闲量,高峰时能快速扩容,超限时及时拒绝而非雪崩 。
我用JMeter做了组对比实验,结果很有意思:
结论很明显:当业务负载上升时,连接池容量必须跟上。盲目追求“轻量”只会导致服务不可用。
最终推荐配置模板:
spring:
datasource:
druid:
initial-size: 5
min-idle: 5
max-active: 50
max-wait: 5000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 1800000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
filters: stat,wall,slf4j
适用于大多数中大型Web应用。
Druid的监控不是摆设,而是故障排查的第一道防线。
通过 /druid/index.html ,你能看到:
但!生产环境千万别直接开放!否则等于把数据库家底亮给全世界看。
正确做法:
@Bean
@Profile({"dev", "test"})
public ServletRegistrationBean<StatViewServlet> statViewServlet() {
ServletRegistrationBean<StatViewServlet> bean =
new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
bean.addInitParameter("allow", "192.168.1.0/24"); // 内网白名单
bean.addInitParameter("loginUsername", "admin");
bean.addInitParameter("loginPassword", "${DRUID_PASSWORD}");
bean.addInitParameter("resetEnable", "false"); // 禁止重置
return bean;
}
并且:
- 只在 dev/test 环境开启;
- 生产如需访问,走Nginx反向代理+IP限制;
- 密码通过环境变量注入,绝不写死。
想看每个接口调了多少次SQL?用它:
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter()
配上 profileEnable=true ,还能看到SQL是由哪个Controller方法发起的,完美支持“N+1查询”问题定位。
定时任务、MQ消费者怎么办?这些非HTTP请求无法被WebStatFilter捕获。
这时就要祭出 StatInterceptor ,用AOP织入任意方法:
@Bean
public StatInterceptor statInterceptor() {
return new StatInterceptor();
}
@Bean
public DefaultPointcutAdvisor statAdvisor()
从此,任何方法里的SQL都能被记录,真正实现无死角监控。
别以为用了MyBatis/Hibernate就安全了,动态拼SQL的情况依然存在。Druid的 wall-filter 就是为此而生。
它不是简单的黑名单匹配,而是通过AST解析识别恶意结构:
spring:
datasource:
druid:
filter:
wall:
enabled: true
db-type: mysql
config:
multi-statement-allow: false
drop-table-allow: false
condition-or-always-success: true
function-blacklist: "sleep,benchmark,load_file"
比如这条SQL:
SELECT * FROM users WHERE id = '1' OR '1'='1'
虽然没出现 union ,但解析后发现条件恒真,结合规则就会被拦截。
甚至像 SLEEP(5) 这种时间盲注,也能通过函数黑名单阻止。
还可以配置表级白名单,防止越权操作:
config:
table-check: true
table-white-set: user,order,product
一旦尝试访问 config 表,立刻抛异常。
这种双保险机制,让攻击者几乎无从下手。
最后,送上一份上线前必查清单:
✅ 多环境配置分离(dev/test/prod)
✅ 敏感信息加密存储(密码、密钥)
✅ 生产禁用StatViewServlet或严格限流
✅ 日志脱敏处理(隐藏密码、身份证号)
✅ Docker/K8s资源配置合理(内存、CPU)
✅ Prometheus + Grafana接入,设置连接数告警
✅ 定期压测验证连接池配置合理性
特别是告警规则:
- alert: HighDatabaseConnectionUsage
expr: druid_connection_active > 40
for: 2m
annotations:
summary: "数据库连接使用过高"
让系统自己告诉你何时该扩容。
Druid的成功告诉我们: 一个好的中间件,不仅要解决问题,更要预防问题 。
它让我们意识到,数据库访问不应该是一个“用了就行”的基础能力,而是一个需要持续观测、主动防护、动态调优的关键系统。
下次当你再次打开 application.yml 准备配置Druid时,希望你能多花一分钟思考:这些参数背后的逻辑是什么?我的系统真的安全吗?监控能不能第一时间发现问题?
毕竟, 真正的稳定性,藏在细节里 。✨
本文还有配套的精品资源,点击获取
简介:Druid是阿里巴巴开源的高性能数据库连接池,集成了SQL解析、监控统计和安全防护等强大功能,广泛应用于Java企业级开发中。 druid-spring-boot-starter 作为Spring Boot的自动化配置启动器,极大简化了Druid的集成流程。本Demo基于Spring Boot 2.x构建,涵盖Druid的核心配置,包括数据源设置、监控页面启用、Web过滤器与SQL拦截器配置,并提供完整的测试代码验证连接池功能。通过该可运行实例,开发者可快速掌握Druid在实际项目中的应用方法,提升数据库访问性能与系统可观测性。
本文还有配套的精品资源,点击获取