JDBC - 为什么不需要手动加载 Driver 实现类了
未经允许不得转载!
看到别人说的不对或者不全,所以开篇文章补充下。
如果只是简单使用 JDBC,那么直接按下面这样做就行了:
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 关闭资源
final String MYSQL_DRIVER = "com.mysql.jdbc.Driver";
final String MYSQL_NEW_DRIVER = "com.mysql.jdbc.Driver";
Class<?> driverImplClass1 = Class.forName(MYSQL_DRIVER);
Class<?> driverImplClass2 = Class.forName(MYSQL_NEW_DRIVER);
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 {
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 方法,然后加载类,然后实例化。
完结撒花
|