从 spring-boot-starter-jdbc 到 DataSource

JDBC 是什么

JDBC 是 Java DataBase Connectivity 的缩写,是由一组用 Java 语言编写的类和接口,用于在 Java 应用程序中与数据库进行交互。

JDBC 只是一套标准规范,具体的实现由各个数据库厂商去实现。对开发者来说其屏蔽了不同数据库之间的区别,可以使用相同的方式(Java API)去操作不同的数据库。不同数据库厂商对 JDBC 的实现就是常说的数据库驱动。如 mysql-connector-java 是连接 MySQL 数据库的驱动。

如何使用 JDBC

大致步骤:

  1. 加载驱动
  2. 获取连接
  3. 执行 SQL
  4. 处理结果
  5. 释放资源

如果你使用 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。主要有三个功能:

  1. 提供了 DataSource 的自动配置
  2. 提供了 JdbcTemplate 等工具类来简化 JDBC 操作
  3. 提供了事务管理

下面主要关注数据源的自动配置。

在项目中引入 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 自动配置原理探究

posted @   Higurashi-kagome  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示