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