MyBatis 源码分析 - 内置数据源 (2)

UnpooledDataSource,从名称上即可知道,该种数据源不具有池化特性。该种数据源每次会返回一个新的数据库连接,而非复用旧的连接。由于 UnpooledDataSource 无需提供连接池功能,因此它的实现非常简单。核心的方法有三个,分别如下:

initializeDriver - 初始化数据库驱动

doGetConnection - 获取数据连接

configureConnection - 配置数据库连接

下面我将按照顺序分节对相关方法进行分析,由于 configureConnection 方法比较简单,因此我把它和 doGetConnection 放在一节中进行分析。下面先来分析 initializeDriver 方法。

3.1 初始化数据库驱动

回顾我们一开始学习使用 JDBC 访问数据库时的情景,在执行 SQL 之前,通常都是先获取数据库连接。一般步骤都是加载数据库驱动,然后通过 DriverManager 获取数据库连接。UnpooledDataSource 也是使用 JDBC 访问数据库的,因此它获取数据库连接的过程也大致如此,只不过会稍有不同。下面我们一起来看一下。

// -☆- UnpooledDataSource private synchronized void initializeDriver() throws SQLException { // 检测缓存中是否包含了与 driver 对应的驱动实例 if (!registeredDrivers.containsKey(driver)) { Class<?> driverType; try { // 加载驱动类型 if (driverClassLoader != null) { // 使用 driverClassLoader 加载驱动 driverType = Class.forName(driver, true, driverClassLoader); } else { // 通过其他 ClassLoader 加载驱动 driverType = Resources.classForName(driver); } // 通过反射创建驱动实例 Driver driverInstance = (Driver) driverType.newInstance(); /* * 注册驱动,注意这里是将 Driver 代理类 DriverProxy 对象注册到 DriverManager 中的, * 而非 Driver 对象本身。DriverProxy 中并没什么特别的逻辑,就不分析。 */ DriverManager.registerDriver(new DriverProxy(driverInstance)); // 缓存驱动类名和实例 registeredDrivers.put(driver, driverInstance); } catch (Exception e) { throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e); } } }

如上,initializeDriver 方法主要包含三步操作,分别如下:

加载驱动

通过反射创建驱动实例

注册驱动实例

这三步都是都是常规操作,比较容易理解。上面代码中出现了缓存相关的逻辑,这个是用于避免重复注册驱动。因为 initializeDriver 放阿飞并不是在 UnpooledDataSource 初始化时被调用的,而是在获取数据库连接时被调用的。因此这里需要做个检测,避免每次获取数据库连接时都重新注册驱动。这个是一个比较小的点,大家看代码时注意一下即可。下面看一下获取数据库连接的逻辑。

3.2 获取数据库连接

在使用 JDBC 时,我们都是通过 DriverManager 的接口方法获取数据库连接。本节所要分析的源码也不例外,一起看一下吧。

// -☆- UnpooledDataSource public Connection getConnection() throws SQLException { return doGetConnection(username, password); } private Connection doGetConnection(String username, String password) throws SQLException { Properties props = new Properties(); if (driverProperties != null) { props.putAll(driverProperties); } if (username != null) { // 存储 user 配置 props.setProperty("user", username); } if (password != null) { // 存储 password 配置 props.setProperty("password", password); } // 调用重载方法 return doGetConnection(props); } private Connection doGetConnection(Properties properties) throws SQLException { // 初始化驱动 initializeDriver(); // 获取连接 Connection connection = DriverManager.getConnection(url, properties); // 配置连接,包括自动提交以及事务等级 configureConnection(connection); return connection; } private void configureConnection(Connection conn) throws SQLException { if (autoCommit != null && autoCommit != conn.getAutoCommit()) { // 设置自动提交 conn.setAutoCommit(autoCommit); } if (defaultTransactionIsolationLevel != null) { // 设置事务隔离级别 conn.setTransactionIsolation(defaultTransactionIsolationLevel); } }

如上,上面方法将一些配置信息放入到 Properties 对象中,然后将数据库连接和 Properties 对象传给 DriverManager 的 getConnection 方法即可获取到数据库连接。

好了,关于 UnpooledDataSource 就先说到这。下面分析一下 PooledDataSource,它的实现要复杂一些。

4.PooledDataSource

PooledDataSource 内部实现了连接池功能,用于复用数据库连接。因此,从效率上来说,PooledDataSource 要高于 UnpooledDataSource。PooledDataSource 需要借助一些辅助类帮助它完成连接池的功能,所以接下来,我们先来认识一下相关的辅助类。

4.1 辅助类介绍

PooledDataSource 需要借助两个辅助类帮其完成功能,这两个辅助类分别是 PoolState 和 PooledConnection。PoolState 用于记录连接池运行时的状态,比如连接获取次数,无效连接数量等。同时 PoolState 内部定义了两个 PooledConnection 集合,用于存储空闲连接和活跃连接。PooledConnection 内部定义了一个 Connection 类型的变量,用于指向真实的数据库连接。以及一个 Connection 的代理类,用于对部分方法调用进行拦截。至于为什么要拦截,随后将进行分析。除此之外,PooledConnection 内部也定义了一些字段,用于记录数据库连接的一些运行时状态。接下来,我们来看一下 PooledConnection 的定义。

class PooledConnection implements InvocationHandler { private static final String CLOSE = "close"; private static final Class<?>[] IFACES = new Class<?>[]{Connection.class}; private final int hashCode; private final PooledDataSource dataSource; // 真实的数据库连接 private final Connection realConnection; // 数据库连接代理 private final Connection proxyConnection; // 从连接池中取出连接时的时间戳 private long checkoutTimestamp; // 数据库连接创建时间 private long createdTimestamp; // 数据库连接最后使用时间 private long lastUsedTimestamp; // connectionTypeCode = (url + username + password).hashCode() private int connectionTypeCode; // 表示连接是否有效 private boolean valid; public PooledConnection(Connection connection, PooledDataSource dataSource) { this.hashCode = connection.hashCode(); this.realConnection = connection; this.dataSource = dataSource; this.createdTimestamp = System.currentTimeMillis(); this.lastUsedTimestamp = System.currentTimeMillis(); this.valid = true; // 创建 Connection 的代理类对象 this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {...} // 省略部分代码 }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywxws.html