【Spring源码】JDBC数据源访问实现

一、阅读线索

开始我们今天的对Spring的【模块阅读 】,来看看Data Access的JDBC模块是怎么设计的。

源码阅读前,我们要先思考下本次的阅读线索

  1. JDBC模块有什么作用
  2. 该模块是怎么设计的
  3. 我们从这个模块可以学到什么

二、探索

关于阅读线索一 ,我们可以很轻松地从官网文档里找到答案。

Data Access with JDBC

也就是提供了数据访问的能力,我们可以使用其来进行各种数据库访问与操作。

先整体看下代码的组织结构,探索下阅读线索二。

主要分为了4个部分,结合官方文档,我们总结下。

  1. core:包含JdbcTemplate及其各种回调接口。
  2. datasource:包含一个易于DataSource访问的实用程序类和各种简单的DataSource实现。子包embedded提供了对使用 Java 数据库引擎(例如 HSQL、H2 和 Derby)创建嵌入式数据库的支持。
  3. object:包含将 RDBMS 查询、更新和存储过程表示为线程安全、可重用对象的类。
  4. support:提供SQLException 翻译功能和一些实用程序类。

也就是说,核心设计 为datasource包提供数据源的访问实现 ,而core包提供JDBC规范的数据库访问模板

1.数据源访问实现

先来看看datasource是如何实现数据源访问 的,既然是要访问,那首先需要初始化,再建立连接

我们定位初始化类DataSourceInitializer的exectute方法。

java 复制代码
public class DataSourceInitializer implements InitializingBean, DisposableBean {

	@Nullable
	private DataSource dataSource;

	/**
	 * The {@link DataSource} for the database to populate when this component
	 * is initialized and to clean up when this component is shut down.
	 * <p>This property is mandatory with no default provided.
	 * @param dataSource the DataSource
	 */
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	private void execute(@Nullable DatabasePopulator populator) {
		Assert.state(this.dataSource != null, "DataSource must be set");
		if (this.enabled && populator != null) {
			DatabasePopulatorUtils.execute(populator, this.dataSource);
		}
	}
}

发现其实数据源是从外部传入 的,可以看到DatabasePopulatorUtils.execute的入参是dataSource

java 复制代码
public interface DataSource extends CommonDataSource, Wrapper {
    Connection getConnection() throws SQLException;

    Connection getConnection(String var1, String var2) throws SQLException;

    PrintWriter getLogWriter() throws SQLException;

    void setLogWriter(PrintWriter var1) throws SQLException;

    void setLoginTimeout(int var1) throws SQLException;

    int getLoginTimeout() throws SQLException;

    default ConnectionBuilder createConnectionBuilder() throws SQLException {
        throw new SQLFeatureNotSupportedException("createConnectionBuilder not implemented");
    }
}

DataSource对象提供了获取连接的接口 ,我们来看看是如何获取到连接,从而实现数据源访问的

查看getConnection方法的子类实现

那获取连接的具体实现应该就在子类AbstractDriverBasedDataSource中。

java 复制代码
	/**
	 * Getting a Connection using the nasty static from DriverManager is extracted
	 * into a protected method to allow for easy unit testing.
	 * @see java.sql.DriverManager#getConnection(String, java.util.Properties)
	 */
	protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException {
		return DriverManager.getConnection(url, props);
	}

我们最终在AbstractDriverBasedDataSource的调用链 上找到了该方法。可以看到是通过传入数据库url和各类参数如密码等 来获取连接,从而实现数据源的访问。

2.数据源访问模板

到这我们就完成了线索二的第一部分,我们再看看看线索二的下一部分:

core包提供JDBC规范的数据库访问模板

我们直接找到JdbcTemplate实现类,看下类的组织结构。

这其中的每一个查询、更新方法就是遵循JDBC规范的模板方法 。Spring就是通过这些模板方法来对数据源数据进行操作

到这我们就解决了阅读线索二

jdbc模块的核心设计就是两部分:

  1. 一是实现对数据源的访问连接。
  2. 二是通过JDBC规范的模板对数据源数据进行操作。

三、总结

我们再来看看阅读线索三 ,这方面我们从设计模式进行入手。

阅读线索三:从这个模块可以学到什么

我们看下以下代码,PreparedStatement实例的是由PreparedStatementCreator实现的。

java 复制代码
	@Nullable
	private <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action, boolean closeResources)
			throws DataAccessException {

		Assert.notNull(psc, "PreparedStatementCreator must not be null");
		Assert.notNull(action, "Callback object must not be null");
		if (logger.isDebugEnabled()) {
			String sql = getSql(psc);
			logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
		}

		Connection con = DataSourceUtils.getConnection(obtainDataSource());
		PreparedStatement ps = null;
		try {
			ps = psc.createPreparedStatement(con);
			applyStatementSettings(ps);
			T result = action.doInPreparedStatement(ps);
			handleWarnings(ps);
			return result;
		}
		......
}	

再来看看PreparedStatementCreator接口,一共有三个子类实现

java 复制代码
@FunctionalInterface
public interface PreparedStatementCreator {

	/**
	 * Create a statement in this connection. Allows implementations to use
	 * PreparedStatements. The JdbcTemplate will close the created statement.
	 * @param con the connection used to create statement
	 * @return a prepared statement
	 * @throws SQLException there is no need to catch SQLExceptions
	 * that may be thrown in the implementation of this method.
	 * The JdbcTemplate class will handle them.
	 */
	PreparedStatement createPreparedStatement(Connection con) throws SQLException;

}

也就是说PreparedStatement的三种不同实现被封装 到三个子类中,而具体需要哪种实现,只需要传入不同的PreparedStatementCreator实现即可。

这种把不同实现封装起来,需要哪种实现由行为模式 决定,也就是入参决定,这种模式被称为策略模式

createPreparedStatement 接口也称为策略接口

未完待续。。。

好了,今天的分享就到这📚。大家能否感受到通过按【模块阅读】这种方式来阅读源码的乐趣呢

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

相关推荐
想用offer打牌2 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX4 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
爬山算法4 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
草梅友仁5 小时前
墨梅博客 1.4.0 发布与开源动态 | 2026 年第 6 周草梅周报
开源·github·ai编程
Cobyte6 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
程序员侠客行6 小时前
Mybatis连接池实现及池化模式
java·后端·架构·mybatis
Honmaple6 小时前
QMD (Quarto Markdown) 搭建与使用指南
后端