More Related Content Similar to Jdbc中驱动加载的过程分析(下)
Similar to Jdbc中驱动加载的过程分析(下) (20) Jdbc中驱动加载的过程分析(下)1. JDBC 中驱动加载的过程分析(下)
江苏 无锡 缪小东
本篇主要用几个开源数据库的驱动讲述驱动是如何加载的,以及“可插拔”机制等。
由于文章篇幅有限,本文章并不是专门研究开源源代码!因此我们仅研究那些与数据库驱动加载直
接相关的方法。在下面的几个开源软件的驱动中,我们主要关注驱动类的 getConnection()方法。
一、几个开源数据库的驱动类
以下第一个是 smallsql 中驱动类 SSDriver 的源代码:
package smallsql.database;
import java.sql.*;
import java.util.Properties;
public class SSDriver implements Driver {
static SSDriver drv;
static {
try{
drv = new SSDriver();
java.sql.DriverManager.registerDriver(drv);
}catch(Throwable e){}
}
public Connection connect(String url, Properties info) throws SQLException {
if(!acceptsURL(url)) return null;
……
return new SSConnection( (idx > 0) ? url.substring(idx+1) : null);
}
……
}
从上面红色的部分可以看到:这是一个静态语句块(static block) ,这意味着该语句是在类构造完成
前完成的(关于语句块的加载请阅读《Think in java》。即调用 class.forName(“smallsql.database.SSDriver”)语句
)
时,会首先创建一个 SSDriver 的实例,并且将其向驱动管理器(DriverManager)注册。这样就完成驱动的注
册了。
从上面的蓝色的代码可以看出:驱动的连接方法返回的是一个具体的 SSConnection 对象。而在前面
研究的 Driver 接口中返回的是 Connection 接口,这是不茅盾的,SSConnection 对象实现了 Connection 接口。
再下面一个是 HSqlD 中的驱动类的源代码:
package org.hsqldb;
import java.sql.*;
import java.util.Properties;
import org.hsqldb.jdbc.jdbcConnection;
import org.hsqldb.persist.HsqlDatabaseProperties;
import org.hsqldb.persist.HsqlProperties;
public class jdbcDriver implements Driver {
1
2. public Connection connect(String url, Properties info) throws SQLException {
return getConnection(url, info);
}
public static Connection getConnection(String url, Properties info) throws SQLException {
HsqlProperties props = DatabaseURL.parseURL(url, true);
if (props == null) {
throw new SQLException(Trace.getMessage(Trace.INVALID_JDBC_ARGUMENT));
} else if (props.isEmpty()) {
return null;
}
props.addProperties(info);
return new jdbcConnection(props);
}
static {
try {
DriverManager.registerDriver(new jdbcDriver());
} catch (Exception e) {}
}
}
蓝色的依然是建立连接的部分,依然返回某个具体的实现 java.sql.Connection 接口的对象。是设计
模式中的哪个工厂啊(一般工厂、抽象工厂、工厂方法还是什么都不是啊) !
红色的同样是一个静态语句块,同样完成该驱动的注册。
两个开源数据库的驱动竟然如此相似,是不是其它的就不一样呢!看下面是 mckoi 中的驱动类:
package com.mckoi;
public class JDBCDriver extends com.mckoi.database.jdbc.MDriver {
static {
com.mckoi.database.jdbc.MDriver.register();
}
public JDBCDriver() {
super();
// Or we could move driver registering here...
}
}
红色的部分又是完成相同的工作――在类装载完成前向驱动管理器注册驱动,只不过这次是调用
MDriver 的 register 方法罢了。那么该驱动建立连接是否也一样呢,当然一样啦!不信你看下面的代码:
package com.mckoi.database.jdbc;
……
public class MDriver implements Driver {
……
private static boolean registered = false;
public synchronized static void register() {
if (registered == false) {
try {
2
3. java.sql.DriverManager.registerDriver(new MDriver());
registered = true;
}catch (SQLException e) {
e.printStackTrace();
}
}
}
public Connection connect(String url, Properties info) throws SQLException {
if (!acceptsURL(url)) { return null; }
DatabaseInterface db_interface;
int row_cache_size;
int max_row_cache_size;
……
MConnection connection = new MConnection(url, db_interface, row_cache_size, max_row_cache_size);
……
return connection;
}
}
从以上三个开源数据库驱动的源代码可以看出: 在调用 Class.forName(“XXXDriver”)时,完成了将具体
的驱动程序向 JDBC API 中驱动管理器的注册, 该注册方法在类构造完成前完成, 一般使用静态语句块。
在调用 DriverManager 的 getConnection 方法时,一般先在已注册的驱动中查找可以了解此 URL 的驱动,
然后调用该驱动的 connect 方法,从而建立连接,返回的连接都是一个实现 java.sql.Connection 接口的具
体类。下面是该过程的时序图。
二、JDBC 中驱动加载的时序图
以上是 JDBC 中驱动加载的时序图。时序图主要有以下 7 个动作:
1. 客户调用 Class.forName(“XXXDriver”)加载驱动。
2. 此时此驱动类首先在其静态语句块中初始化此驱动的实例,
3. 再向驱动管理器注册此驱动。
3
4. 4. 客户向驱动管理器 DriverManager 调用 getConnection 方法,
5. DriverManager 调用注册到它上面的能够理解此 URL 的驱动建立一个连接,
6. 在该驱动中建立一个连接,一般会创建一个对应于数据库提供商的 XXXConnection 连接对象,
7. 驱 动 向 客 户 返 回 此 连 接 对 象 , 不 过 在 客 户 调 用 的 getConnection 方 法 中 返 回 的 为 一 个
java.sql.Connection 接口,而具体的驱动返回一个实现 java.sql.Connection 接口的具体类。
以上就是驱动加载的全过程。由此过程我们可以看出 JDBC 的其它一些特点。
三、JDBC 的架构
在《教你建立简单 JDBC 程序》一篇中,讲述了一般 JDBC 的几个步骤。通过本篇的介绍,我将此
程序分为以下几部分:
上图中,蓝色的即为本章前面介绍的 JDBC 驱动加载的细节部分。看看下面的部分:左面的很明显
吧!是 java.sql 包中的接口吧!它是抽象的!右边呢?通过驱动管理器 DriverManager 得到的是一个实现
java.sql.Connection 接口的具体类吧!
(不知道啊!前面不是讲过了吗!)因此我们可以可以注意到左右分
别是抽象的和具体的。 这种抽象和具体的连接是由 java 的 RTTI 支持的,
( 不懂可以阅读 《Think in java》。
)
在接下来的结果集的处理 rs 也是抽象的吧!
因此,在写 JDBC 程序时,即使我们使用不同数据库提供商的数据库我们只要改变驱动类的地址,
和具体连接的 URL 及其用户名和密码,其它几乎不用任何修改,就可以完成同样的工作!方便吧!
这意味着什么呢?我们其实是在针对抽象接口编程,只要知道接口的调用顺序,以及其中的主要方
法,我们就可以迅速学会 JDBC 编程了!
同时,我们只要对不同数据库提供商的驱动类使用 Class.forName(“XXXDriver”)就可以加载驱动,
其细节你根本不用关注――JDBC Framework 已经全为你搞定了!应用程序对于不同提供商的数据库是
无需太多改动的,因而其是“可插拔”的!整个 J2EE 就是一个高层的 API――抽象接口,用户可以使
4