【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 接口也称为策略接口

未完待续。。。

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

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

相关推荐
uzong4 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程5 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研5 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
草梅友仁6 小时前
草梅 Auth 1.4.0 发布与 ESLint v9 更新 | 2025 年第 33 周草梅周报
vue.js·github·nuxt.js
AntBlack7 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9657 小时前
pip install 已经不再安全
后端