掘金 后端 ( ) • 2024-04-11 17:39

一、阅读线索

开始我们今天的对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方法。

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

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中。

	/**
	 * 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实现的。

	@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接口,一共有三个子类实现

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

未完待续。。。

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

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