从 spring-boot-starter-jdbc 到 DataSource
JDBC 是什么
JDBC 是 Java DataBase Connectivity 的缩写,是由一组用 Java 语言编写的类和接口,用于在 Java 应用程序中与数据库进行交互。
JDBC 只是一套标准规范,具体的实现由各个数据库厂商去实现。对开发者来说其屏蔽了不同数据库之间的区别,可以使用相同的方式(Java API)去操作不同的数据库。不同数据库厂商对 JDBC 的实现就是常说的数据库驱动。如 mysql-connector-java 是连接 MySQL 数据库的驱动。
如何使用 JDBC
大致步骤:
- 加载驱动
- 获取连接
- 执行 SQL
- 处理结果
- 释放资源
如果你使用 Maven,可以在 pom.xml 文件中添加驱动依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.32</version> </dependency>
然后如下使用 JDBC:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JdbcExample { public static void main(String[] args) { // 数据库 URL String url = "jdbc:mysql://localhost:3306/test"; // 数据库用户名和密码 String user = "root"; String password = "123456"; // 声明连接和其他对象 Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { // 1. 加载 JDBC 驱动 Class.forName("com.mysql.cj.jdbc.Driver"); // 2. 创建连接 connection = DriverManager.getConnection(url, user, password); // 3. 创建 Statement 对象 statement = connection.createStatement(); // 4. 执行查询 String sql = "SELECT * FROM user"; resultSet = statement.executeQuery(sql); // 5. 处理结果集 while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); System.out.println("ID: " + id + ", Name: " + name); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { // 6. 关闭资源 try { if (resultSet != null) resultSet.close(); if (statement != null) statement.close(); if (connection != null) connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
DataSource 接口
DataSource 接口(javax.sql.DataSource)是 JDBC 2.0 引入的,用于表示数据库连接工厂。功能上,可以把 DriverManager 看作是 JDBC 官方提供的 DataSource 实现(虽然实际上 DriverManager 并没有实现 DataSource 接口)。
DataSource 的常见实现有 DBCP、C3P0、HikariCP、Druid 等,这些实现通常使用了池化技术来提高连接利用率。
spring-boot-starter-jdbc 数据源配置
spring-boot-starter-jdbc 是 Spring Boot 提供的用于简化 JDBC 操作的 starter。主要有三个功能:
- 提供了 DataSource 的自动配置
- 提供了 JdbcTemplate 等工具类来简化 JDBC 操作
- 提供了事务管理
下面主要关注数据源的自动配置。
在项目中引入 spring-boot-starter-jdbc 后,会自动配置一个 DataSource 的 Bean,并提供一个 JdbcTemplate 的 Bean:
@Slf4j @Component public class Init implements CommandLineRunner { @Resource private DataSource dataSource; @Resource private JdbcTemplate jdbcTemplate; @Override public void run(String... args) { log.info(dataSource.getClass().getName()); log.info(jdbcTemplate.getClass().getName()); } }
com.zaxxer.hikari.HikariDataSource org.springframework.jdbc.core.JdbcTemplate
可以看到,DataSource 的默认实现类是 HikariDataSource。也就是 Spring Boot 默认使用了 HikariCP 作为连接池。
进入 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,其中的 PooledDataSourceConfiguration 会根据配置文件中的 spring.datasource.type 属性来决定使用哪个 DataSource 实现类(注意 @Import 注解):
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") @AutoConfigureBefore(SqlInitializationAutoConfiguration.class) @EnableConfigurationProperties(DataSourceProperties.class) @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.class, DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.class }) public class DataSourceAutoConfiguration { // ... @Configuration(proxyBeanMethods = false) @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration { } // ... }
比如打开 DataSourceConfiguration.Hikari 可以看到:
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(HikariDataSource.class) @ConditionalOnMissingBean(DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) static class Hikari { @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") HikariDataSource dataSource(DataSourceProperties properties) { HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class); if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return dataSource; } }
因为 Spring Boot 默认引入了 HikariCP,而没有引入其他连接池的依赖比如 DBCP2,所以在 spring.datasource.type 没有配置的情况下,会使用 HikariDataSource。
再看上面的 createDataSource 方法,该方法会根据配置文件中配置的 url 等属性来创建 HikariDataSource。
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) { return (T) properties.initializeDataSourceBuilder().type(type).build(); }
public DataSourceBuilder<?> initializeDataSourceBuilder() { return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName()) .url(determineUrl()).username(determineUsername()).password(determinePassword()); }
要切换其他连接池,比如 Druid,可以引入 Druid 的依赖:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.9</version> </dependency>
然后在配置文件中指定 spring.datasource.type 为 com.alibaba.druid.pool.DruidDataSource:
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource
如果项目中使用了 dynamic-datasource,进入其自动配置类 DynamicDataSourceAutoConfiguration,可以看到其会在容器中注册 DynamicRoutingDataSource:
@Bean @ConditionalOnMissingBean public DataSource dataSource() { DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(); dataSource.setPrimary(properties.getPrimary()); dataSource.setStrict(properties.getStrict()); dataSource.setStrategy(properties.getStrategy()); dataSource.setP6spy(properties.getP6spy()); dataSource.setSeata(properties.getSeata()); return dataSource; }
DynamicRoutingDataSource 继承自 AbstractRoutingDataSource,AbstractRoutingDataSource 继承自 AbstractDataSource,AbstractDataSource 实现了 DataSource 接口。
DynamicRoutingDataSource 是能够通过 @DS 注解切换不同的数据源的底层核心。
参考:从 jdbc 到 spring-boot-starter-jdbc、什么是 DataSource?什么又是 DruidDataSource?、SpringBoot 2.X 集成 jdbc 自动配置原理探究
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义