第2篇-Druid连接池的整体架构
约 1749 字大约 6 分钟
2026-05-24
四、DruidConnectionHolder:连接的"身份证"
在 Druid 中,每个数据库连接都被包装成一个 DruidConnectionHolder 对象。它不仅仅包含连接本身,还记录了连接的"元数据"。
// 类路径:com.alibaba.druid.pool.DruidConnectionHolder
public final class DruidConnectionHolder {
// 【核心】所属数据源
protected final DruidAbstractDataSource dataSource;
// 【核心】物理连接
protected final Connection conn;
// 【核心】连接 ID(唯一标识)
protected final long connectionId;
// 【核心】连接创建时间
protected final long connectTimeMillis;
// 【核心】上次活跃时间(用于判断空闲时间)
protected volatile long lastActiveTimeMillis;
// 【核心】被使用的次数
protected long useCount = 0;
// 【核心】预编译语句缓存
protected PreparedStatementPool statementPool;
// 【核心】Statement 跟踪(用于回收连接时关闭 Statement)
protected final List<Statement> statementTrace = new ArrayList<Statement>(2);
// 【核心】默认配置(用于重置连接状态)
protected final boolean defaultAutoCommit;
protected final boolean defaultReadOnly;
protected final int defaultTransactionIsolation;
}白话文解释:
DruidConnectionHolder就像连接的"身份证",记录了连接的所有重要信息。lastActiveTimeMillis字段非常重要:它用来计算连接的空闲时间。如果空闲时间太长,就会被DestroyConnectionThread回收。useCount字段记录了连接被使用了多少次。如果达到了phyMaxUseCount,连接也会被回收(防止连接"过劳死")。statementPool字段用于缓存PreparedStatement。这样,下次执行相同的 SQL 时,就不需要重新创建PreparedStatement了,能提升性能。
五、Spring Boot 中配置 Druid:自动装配的"魔法"
5.1 引入依赖
首先,在 pom.xml 中引入 Druid 的 Spring Boot Starter:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.20</version>
</dependency>5.2 配置 application.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 【核心】指定使用 Druid 数据源
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
username: root
password: password
druid:
# 【核心】连接池配置
initial-size: 5 # 初始连接数
min-idle: 5 # 最小空闲连接数
max-active: 20 # 最大连接数
max-wait: 60000 # 获取连接的最大等待时间(毫秒)
# 【核心】连接检测配置
validation-query: SELECT 1 FROM DUAL # 验证连接的 SQL(Oracle 需要写 SELECT 1 FROM DUAL)
test-on-borrow: false # 借出时是否检测(生产环境建议 false,提升性能)
test-on-return: false # 归还时是否检测
test-while-idle: true # 空闲时是否检测(建议 true)
# 【核心】连接回收配置
time-between-eviction-runs-millis: 60000 # 回收线程运行间隔(毫秒)
min-evictable-idle-time-millis: 300000 # 最小空闲时间(5 分钟)
# 【核心】监控配置
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: admin
# 【核心】过滤器配置
filters: stat,wall,log4j2 # 启用监控统计、防火墙、日志过滤器5.3 Druid 自动装配原理
当你引入 druid-spring-boot-starter 后,Spring Boot 会自动加载 DruidDataSourceAutoConfigure 配置类:
// 类路径:com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class) // 【核心】在 Spring Boot 的默认配置之前执行
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class,
DruidStatFilterConfiguration.class,
DruidWebStatFilterConfiguration.class,
DruidWebStatServletConfiguration.class,
DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
@Bean(initMethod = "init", destroyMethod = "close") // 【核心】init 方法会在 Bean 初始化时调用
@ConditionalOnMissingBean
public DataSource dataSource() {
// 【核心】创建 DruidDataSource
return DruidDataSourceBuilder.create().build();
}
}白话文解释:
@AutoConfigureBefore(DataSourceAutoConfiguration.class)这个注解很重要!它保证了:Druid 的配置先于 Spring Boot 默认的配置执行。这样,Spring Boot 就不会使用默认的 HikariCP,而是使用 Druid。@Bean(initMethod = "init")保证了:当 Spring 创建DruidDataSourceBean 时,会自动调用init()方法完成连接池的初始化。- 这就好比你装修房子:你先跟 Druid 说"我要装这种风格的瓷砖"(配置),等装修工人(Spring Boot)来的时候,他看到你的要求,就按你的要求装,而不是用默认的瓷砖。
六、埋下伏笔:MyBatis 如何获取连接?
说到这里,你可能会问:MyBatis 是如何从 Druid 连接池中获取连接的呢?
这个问题,我们会在系列三(MyBatis 系列)中详细解答。这里先预告一下核心流程:
- MyBatis 通过
SqlSession执行 SQL。 SqlSession内部使用了Executor(执行器)。Executor在执行 SQL 前,会通过SpringManagedTransaction获取数据库连接。SpringManagedTransaction会通过DataSourceUtils.getConnection()获取连接。DataSourceUtils.getConnection()最终调用dataSource.getConnection(),也就是 Druid 的getConnection()方法。
下一篇文章的预告:《数据源系列(三):连接的借与还(getConnection 全流程)》
七、总结与思考
今天我们一起深入了 Druid 连接池的整体架构,了解了它的继承体系、初始化流程、线程模型和核心组件。
让我们用几个问题来检验一下今天的学习成果:
Druid 的继承体系是怎样的?
DruidDataSource继承自DruidAbstractDataSource,实现了DataSource等接口。DruidAbstractDataSource负责存放配置项。DruidDataSource负责连接池的具体管理。
Druid 的初始化流程是怎样的?
- 调用
init()方法。 - 创建初始连接(根据
initialSize配置)。 - 启动后台线程(
CreateConnectionThread、DestroyConnectionThread)。 - 注册 JMX MBean。
- 调用
Druid 的线程模型是怎样的?
CreateConnectionThread:负责创建新连接。DestroyConnectionThread:负责销毁过期连接。LogStatsThread:负责打印统计信息。
如何在 Spring Boot 中配置 Druid?
- 引入
druid-spring-boot-starter依赖。 - 在
application.yml中配置 Druid 相关参数。 - Spring Boot 会自动装配
DruidDataSource。
- 引入
八、扩展阅读
【扩展点】自定义过滤器
Druid 支持通过过滤器链扩展功能。你可以自定义过滤器,实现特殊的业务逻辑:
// 【扩展点】自定义过滤器示例
public class MyCustomFilter extends FilterEventAdapter {
// 【注意】在连接创建时执行
@Override
public void connection_connectBefore(DruidDataSource dataSource, Connection connection) {
System.out.println("创建连接前...");
}
// 【注意】在 SQL 执行时执行
@Override
public void statement_executeBefore(StatementProxy statement, String sql) {
System.out.println("执行 SQL 前:" + sql);
}
}场景:你可以在自定义过滤器中实现:
- SQL 审计(记录所有执行的 SQL)。
- 慢查询监控(统计执行时间超过 1 秒的 SQL)。
- 动态数据源切换(根据 SQL 类型,路由到不同的数据源)。
如何启用自定义过滤器? 在 application.yml 中配置:
spring:
datasource:
druid:
filters: stat,wall,com.example.MyCustomFilter好了,今天的文章就到这里。下一篇,我们将深入 getConnection() 方法的源码,看看 Druid 是如何"借出"和"回收"连接的。敬请期待!
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发!你的支持是我继续创作的动力!
系列文章目录:
- 系列一:Spring 事务系列(已完成)
- 第1篇:@Transactional 注解的"前世今生"
- 第2篇:AOP 代理 —— 事务的"隐身衣"
- 第3篇:事务传播机制源码深度解析
- 第4篇:事务隔离级别的实现原理
- 第5篇:事务管理器的设计哲学
- 第6篇:事务资源的绑定与传递
- 第7篇:提交、回滚与异常规则
- 系列二:数据源系列(进行中)
- 第1篇:JDBC 规范与 DriverManager 的 legacy(已完成)
- 第2篇:Druid 连接池的整体架构(本文)
- 第3篇:连接的借与还(getConnection 全流程)
- 第4篇:Druid 过滤链与监控统计
- 第5篇:MySQL 驱动的 JDBC 实现
- 第6篇:连接的生命周期与事务边界
- 系列三:MyBatis 系列(待开始)
- 系列四:总结系列(待开始)