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

未完待续。。。

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

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

相关推荐
Pandaconda几秒前
【计算机网络 - 基础问题】每日 3 题(十)
开发语言·经验分享·笔记·后端·计算机网络·面试·职场和发展
程序员大金36 分钟前
基于SpringBoot+Vue+MySQL的养老院管理系统
java·vue.js·spring boot·vscode·后端·mysql·vim
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS网上购物商城(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
Ylucius1 小时前
JavaScript 与 Java 的继承有何区别?-----原型继承,单继承有何联系?
java·开发语言·前端·javascript·后端·学习
ღ᭄ꦿ࿐Never say never꧂2 小时前
微服务架构中的负载均衡与服务注册中心(Nacos)
java·spring boot·后端·spring cloud·微服务·架构·负载均衡
.生产的驴2 小时前
SpringBoot 消息队列RabbitMQ 消息确认机制确保消息发送成功和失败 生产者确认
java·javascript·spring boot·后端·rabbitmq·负载均衡·java-rabbitmq
海里真的有鱼2 小时前
Spring Boot 中整合 Kafka
后端
布瑞泽的童话2 小时前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
写bug写bug2 小时前
6 种服务限流的实现方式
java·后端·微服务