JDBC - 为什么不需要手动加载 Driver 实现类了

JDBC - 为什么不需要手动加载 Driver 实现类了

未经允许不得转载!

看到别人说的不对或者不全,所以开篇文章补充下。

如果只是简单使用 JDBC,那么直接按下面这样做就行了:

// 实例化 MySQL 的 Driver 实现类
java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();
// 建立连接
Connection connect = driver.connect(url, properties);
// ... 各种操作

上面这种做法耦合性太强了,如果MySQL的 jdbc Driver 实现类变了呢?手动改呗!
那么如果需要同时使用多个不同的数据库呢?

所以 java 提供了一个简单的 jdbc Drivers 管理类:java.sql.DriverManager
通过java.sql.DriverManager#registerDriver()注册上面例子的实例
通过java.sql.DriverManager#getConnection()建立上面例子中的数据库连接
通过java.sql.DriverManager#getDrivers()方法获取所有注册的示例
…更多方法自己看

也就是说只要调用DriverManager#registerDriver()就可以把实例注册进去,然后通过java.sql.DriverManager#getConnection()获取连接(只有一个实例或者第一个注册的实例就是所需的),如果有多个,则通过java.sql.DriverManager#getDrivers()获取所有的实例,然后自己选择需要的 Driver 实例,然后调用 connect 方法建立连接。

那么问题就来了,你们在使用 jdbc 的时候有写过DriverManager#registerDriver()这样的代码吗?

先看看远古时期使用 jdbc 的5步走是怎么做的吗?(远古时期:java1.2+ 包括)

  • 1/5 Class.forName(driver 实现类的全名);
  • 2/5 建立连接 Connection
  • 3/5 获取 Statement
  • 4/5 执行并获取 ResultSet
  • 5/5 关闭资源
// jdbc 1/5 Class.forName(driver 实现类的全名);

final String MYSQL_DRIVER = "com.mysql.jdbc.Driver";
final String MYSQL_NEW_DRIVER = "com.mysql.jdbc.Driver";

// 方式1,显式手动单独加载两个 jdbc 实现类
Class<?> driverImplClass1 = Class.forName(MYSQL_DRIVER);
Class<?> driverImplClass2 = Class.forName(MYSQL_NEW_DRIVER);
// ... more

// 方式2,隐式手动批量加载多个 jdbc 实现类
 多个驱动实现类用":"隔开
String DRIVERS = MYSQL_DRIVER + ":" + MYSQL_NEW_DRIVER;
String defaultDrivers = System.getProperty("jdbc.drivers");
if (defaultDrivers == null || defaultDrivers.length() == 0) {
    System.setProperty("jdbc.drivers", DRIVERS);
}
else {
    // 合并后再设置配置
}

1/5只是加载了类,所以 Driver 实现类的静态代码块(或者有静态字段)很重要。
以MySQL 5.1.48 的 Driver 实现类com.mysql.jdbc.Driver为例:

// 去掉了一些不重要的代码和注释
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
    		// 看到了没有,看到了没有,看到了没有 重要的事情说三遍
            java.sql.DriverManager.registerDriver(new Driver());
    }
    public Driver() throws SQLException {}
}
public class NonRegisteringDriver implements java.sql.Driver {
    static {
    		// AbandonedConnectionCleanupThread 这个类不用看
    		// 简单说就是启动一个守护线程去跟踪被垃圾回收器回收的 Connection
    		// 如果资源没有关闭则进行相关操作,从而节约资源以及防止异常情况发生
            Class.forName(AbandonedConnectionCleanupThread.class.getName());
    }
}

再看看现代是如何注册 Driver 实现类的?(现代:java1.6+ 包括)
要说起这个得先说起java.sql.DriverManager这个类,这个类在被加载时,会执行DriverManager#loadInitialDrivers()方法,而远古时代提供的批量注册 Driver 实现类的方式就是在这里面实现的,代码很简单不说了。
这里重点说下另外的一个方式,也就是包含ServiceLoader.load(Driver.class)代码的那一整块,这块代码是根据 java 6 提供的 SPI 功能而增加的:

  1. 在项目下提供一个META-INF/services/java.sql.Driver文件,可以参考 MySQL 的那个实现
  2. 调用ServiceLoader.load(Driver.class);获取 service,此时资源还未被加载(因为是懒加载的)
  3. 通过迭代器进行遍历 (java.util.ServiceLoader.LazyIterator),迭代器在执行next方法的时候最终会调用nextService方法,然后加载类,然后实例化。

完结撒花

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值