jdbc加载驱动 java_JDBC 驱动加载过程

JDBC驱动加载过程包括通过Class.forName()将驱动类加载到内存并执行静态代码块,注册到DriverManager。Driver接口规定了驱动的功能,如acceptsURL()和connect()。DriverManager负责管理驱动,根据URL获取合适的Driver并创建数据库连接。手动加载驱动较为繁琐,通常使用DriverManager简化操作,其getDriver()方法根据URL返回对应的Driver。jdbc.drivers系统属性可以预加载驱动。
摘要由CSDN通过智能技术生成

JDBC 驱动加载过程

概述

一般情况下,在应用程序中进行数据库连接,调用 JDBC 接口,首先要将特定厂商的 JDBC 驱动实现加载到系统内存中,然后供系统使用。基本结构图如下:

391e9394e1695a9d2cf5d1362c8fcdde.png

驱动加载入内存的过程

这里所谓的驱动,其实就是实现了 java.sql.Driver 接口的类。如 oracle 的驱动类是 oracle.jdbc.driver.OracleDriver.class(此类可以在 oracle 提供的 JDBC jar 包中找到),此类实现了 java.sql.Driver 接口。

由于驱动本质上还是一个 class,将驱动加载到内存和加载普通的 class 原理是一样的: 使用 Class.forName("driverName")。以下是将常用的数据库驱动加载到内存中的代码:

//加载Oracle数据库驱动

Class.forName("oracle.jdbc.driver.OracleDriver");

//加载SQL Server数据库驱动

Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

//加载MySQL 数据库驱动

Class.forName("com.mysql.jdbc.Driver");

注意:Class.forName() 将对应的驱动类加载到内存中,然后执行内存中的 static 静态代码段,代码段中,会创建一个驱动 Driver 的实例,放入 DriverManager 中,供 DriverManager 使用。

所以这就是为什么,如果使用ClassLoader的话,需要自己创建实例,但是使用forName就不用

例如,在使用 Class.forName() 加载 oracle 的驱动 oracle.jdbc.driver.OracleDriver 时,会执行 OracleDriver 中的静态代码段,创建一个 OracleDriver 实例,然后调用 DriverManager.registerDriver() 注册:

static {

Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");

try {

if (defaultDriver == null) {

//创建一个OracleDriver实例,然后注册到DriverManager中

defaultDriver = new OracleDriver();

DriverManager.registerDriver(defaultDriver);

}

} catch (RuntimeException localRuntimeException) {

} catch (SQLException localSQLException) {

}

Driver 的功能

java.sql.Driver 接口规定了 Driver 应该具有以下功能:

17e40ce1d2e9883cb0756a08d6d5d59b.png

其中:

acceptsURL(String url) 方法用来测试对指定的 url,该驱动能否打开这个 url 连接。driver 对自己能够连接的 url 会制定自己的协议,只有符合自己的协议形式的 url 才认为自己能够打开这个 url,如果能够打开,返回 true,反之,返回 false;

例如:oracle 定义的自己的 url 协议如下:

jdbc:oracle:thin:@//:/ServiceName

jdbc:oracle:thin:@::

oracle 自己的 acceptsURL(String url) 方法如下:

public boolean acceptsURL(String paramString) {

if (paramString.startsWith("jdbc:oracle:")) {

return (oracleDriverExtensionTypeFromURL(paramString) > -2);

}

return false;

}

private int oracleDriverExtensionTypeFromURL(String paramString) {

int i = paramString.indexOf(58) + 1;

if (i == 0) {

return -2;

}

int j = paramString.indexOf(58, i);

if (j == -1) {

return -2;

}

if (!(paramString.regionMatches(true, i, "oracle", 0, j - i))) {

return -2;

}

++j;

int k = paramString.indexOf(58, j);

if (k == -1) {

return -3;

}

String str = paramString.substring(j, k);

if (str.equals("thin")) {

return 0;

}

if ((str.equals("oci8")) || (str.equals("oci"))) {

return 2;

}

return -3;

}

由上可知 oracle 定义了自己应该接收什么类型的 URL,自己能打开什么类型的 URL 连接(注意:这里 acceptsURL(url) 只会校验 url 是否符合协议,不会尝试连接判断 url 是否有效) 。connect(String url,Properties info)方法,创建 Connection 对象,用来和数据库的数据操作和交互,而 Connection 则是真正数据库操作的开始(在此方法中,没有规定是否要进行 acceptsURL() 进行校验)。

手动加载驱动 Driver 并实例化进行数据库操作的例子

public static void driverTest(){

try {

//1.加载oracle驱动类,并实例化

Driver driver = (Driver) Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();

//2.判定指定的URL oracle驱动能否接受(符合oracle协议规则)

boolean flag = driver.acceptsURL("jdbc:oracle:thin:@127.0.0.1:1521:xe");

//标准协议测试

boolean standardFlag1 = driver.acceptsURL("jdbc:oracle:thin:@//:/ServiceName");

boolean standardFlag2 = driver.acceptsURL("jdbc:oracle:thin:@::");

System.out.println("协议测试:"+flag+"\t"+standardFlag1+"\t"+standardFlag2);

//3.创建真实的数据库连接:

String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";

//将连接参数放入properties文件中

Properties props = new Properties();

props.put("user", "louluan");

props.put("password", "123456");

Connection connection = driver.connect(url, props);

//connection 对象用于数据库交互,代码省略。。。。。

} catch (Exception e) {

System.out.println("加载Oracle类失败!");

e.printStackTrace();

} finally{

}

}

​ 上述的手动加载 Driver 并且获取连接的过程稍显笨拙:如果现在我们加载进来了多个驱动 Driver,那么手动创建 Driver 实例,并根据 URL 进行创建连接就会显得代码杂乱无章,并且还容易出错,并且不方便管理。JDBC 中提供了一个 DriverManager 角色,用来管理这些驱动 Driver。

DriverManager 角色

​ 事实上,一般我们操作 Driver,获取 Connection 对象都是交给 DriverManager 统一管理的。DriverManger 可以注册和删除加载的驱动程序,可以根据给定的 url 获取符合 url 协议的驱动 Driver 或者是建立 Conenction 连接(这也就是说,我们可以在DriverManager中注册多个驱动,然后用Url的格式进行区分,从而获得不同的Connection对象),进行数据库交互。

e1bdd3b26210458d365ad31d0b590d6c.png

以下是 DriverManager 的关键方法摘要:

c1b0e30ba36006d8136361dff8995942.png

DriverManager 内部持有这些注册进来的驱动 Driver,由于这些驱动都是 java.sql.Driver 类型,那么怎样才能获得指定厂商的驱动 Driver 呢?答案就在于:java.sql.Driver 接口规定了厂商实现该接口,并且定义自己的 URL 协议。厂商们实现的 Driver 接口通过 acceptsURL(String url) 来判断此 url 是否符合自己的协议 (这段话是作者所说,实际不一定是通过 acceptURL 方法判断,但逻辑顺序是对的,即先判断是否符合协议格式,再试图创建 Connection 实例),如果符合自己的协议,则可以使用本驱动进行数据库连接操作,查询驱动程序是否认为它可以打开到给定 URL 的连接。

使用 DriverManager 获取指定 Driver

对于驱动加载后,如何获取指定的驱动程序呢?这里,DriverManager 的静态方法 getDriver(String url) 可以通过传递给的 URL,返回可以打开此 URL 连接的 Driver。

比如,我想获取 oracle 的数据库驱动,只需要传递形如jdbc:oracle:thin:@::或者 jdbc:oracle:thin:@//:/ServiceName 的参数给 DriverManager.getDriver(String url) 即可:

Driver oracleDriver =DriverManager.getDriver("jdbc:oracle:thin:@::");

实际上,DriverManager.getDriver(String url) 方法是根据传递过来的 URL,遍历它维护的驱动 Driver,依次调用驱动的 Driver 的 acceptsURL(url),如果返回 acceptsURL(url) 返回 true,则返回对应的 Driver:

public static Driver getDriver(String paramString) throws SQLException {

//省略部分代码。。。。

Iterator localIterator = registeredDrivers.iterator();

//遍历注册的驱动

while (localIterator.hasNext()) {

DriverInfo localDriverInfo = (DriverInfo) localIterator.next();

if (isDriverAllowed(localDriverInfo.driver, localClass))

try {

//如果accepsURL() 为true,返回对应的driver

if (localDriverInfo.driver.acceptsURL(paramString)) {

//返回对应的driver

return localDriverInfo.driver;

}

} catch (SQLException localSQLException) {

}

else

println(" skipping: "+ localDriverInfo.driver.getClass().getName());

}

throw new SQLException("No suitable driver", "08001");

//-----省略部分代码

}

使用 DriverManager 注册和取消注册驱动 Driver

在本博文开始的 驱动加载的过程一节中,讨论了当使用 Class.forName("driverName") 加载驱动的时候,会向 DriverManager 中注册一个 Driver 实例。以下代码将验证此说法:

public static void defaultDriver(){

try {

String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";

//1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中

Class.forName("oracle.jdbc.driver.OracleDriver");

//取出对应的oracle 驱动Driver

Driver driver = DriverManager.getDriver(url);

System.out.println("加载类后,获取Driver对象:"+driver);

//将driver从DriverManager中注销掉

DriverManager.deregisterDriver(driver);

//重新通过url从DriverManager中取Driver

driver = DriverManager.getDriver(url);

System.out.println(driver);

} catch (Exception e) {

System.out.println("加载Oracle类失败!");

e.printStackTrace();

} finally{

}

}

以上代码主要分以下几步:

1. 首先是将 oracle.jdbc.driver.OracleDriver 加载到内存中;

2. 然后便调用 DriverManager.getDriver() 去取 Driver 实例;

3. 将 driver 实例从 DriverManager 中注销掉;

4. 尝试再取 对应 url 的 Driver 实例;

上述代码执行的结果如下:

e5359752163495d65e5e798fcf6de545.png

从执行结果看,正好能够验证以上论述:当第四步再次获取对应 url 的 Driver 实例时,由于已经被注销掉了,找不到适当的驱动 Driver,抛出了 "Not suitable driver" 的异常。

将上述的例子稍作变化,在注销掉了静态块创建的 driver 后,往 DriverManager 注册一个自己创建的 Driver 对象实例 (具体步骤请看注释):

public static void defaultDriver(){

try {

String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";

//1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中

Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();

//2.取出对应的oracle 驱动Driver

Driver driver = DriverManager.getDriver(url);

System.out.println("加载类后,获取Driver对象:"+driver);

//3. 将driver从DriverManager中注销掉

DriverManager.deregisterDriver(driver);

//4.此时DriverManager中已经没有了驱动Driver实例,将创建的dd注册到DriverManager中

DriverManager.registerDriver(dd);

//5.重新通过url从DriverManager中取Driver

driver = DriverManager.getDriver(url);

System.out.println("注销掉静态创建的Driver后,重新注册的Driver: "+driver);

System.out.println("driver和dd是否是同一对象:" +(driver==dd));

} catch (Exception e) {

System.out.println("加载Oracle类失败!");

e.printStackTrace();

} finally{

}

}

以下代码运行的结果:

e6dd2e00f8fa93ef81482b606b82dd8f.png

以上代码先创建了一个 Driver 对象,在注销了 DriverManager 中由加载驱动过程中静态创建驱动之后,注册到系统中,现在 DriverManager 中对应 url 返回的 Driver 即是在代码中创建的 Driver 对象。

使用 DriverManager 创建 Connection 连接对象

创建 Connection 连接对象,可以使用驱动 Driver 的 connect(url,props),也可以使用 DriverManager 提供的 getConnection() 方法,此方法通过 url 自动匹配对应的驱动 Driver 实例,然后调用对应的 connect 方法返回 Connection 对象实例。

Driver driver = DriverManager.getDriver(url);

Connection connection = driver.connect(url, props);

上述代码等价于:

Class.forName("oracle.jdbc.driver.OracleDriver");

Connection connection = DriverManager.getConnection(url, props);

jdbc.drivers

DriverManager 作为 Driver 的管理器,它在第一次被使用的过程中(即在代码中第一次用到的时候),它会被加载到内存中,然后执行其定义的 static 静态代码段,在静态代码段中,有一个 loadInitialDrivers() 静态方法,用于加载配置在 jdbc.drivers 系统属性内的驱动 Driver,配置在 jdbc.drivers 中的驱动 driver 将会首先被加载,这些驱动就不需要手动注册了:

static {

loadInitialDrivers();//加载配置在jdbc.drivers系统变量中的驱动driver

println("JDBC DriverManager initialized");

SET_LOG_PERMISSION = new SQLPermission("setLog");

}

private static void loadInitialDrivers() {

String str1;

try {

str1 = (String) AccessController

.doPrivileged(new PrivilegedAction() {

public String run() {

return System.getProperty("jdbc.drivers");//返回jdbc.drivers值

}

});

} catch (Exception localException1) {

str1 = null;

}

//省略部分代码......

if ((str1 == null) || (str1.equals("")))

return;

String[] arrayOfString1 = str1.split(":");

println("number of Drivers:" + arrayOfString1.length);

for (String str2 : arrayOfString1)

try {

//Class.forName加载对应的driver

Class.forName(str2, true, ClassLoader.getSystemClassLoader());

} catch (Exception localException2) {

println("DriverManager.Initialize: load failed: "

+ localException2);

}

}

String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";

//设置值系统变量jdbc.drivers

System.setProperty("jdbc.drivers", "oracle.jdbc.driver.OracleDriver");

//2.通过特定的url获取driver

Driver driver = DriverManager.getDriver(url);

//打印是否存在

System.out.println(driver);

请记住:一定要在第一次使用 DriverManager 之前设置 jdbc.drivers,因为 DriverManager 中的 static 静态代码段只会被执行一次!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值