1. 先看看原生jdbc执行sql的步骤

// 在程序启动的时候需要注册一次mysql驱动,必须引入 mysql-connnector-java 的包
Class.forName("com.mysql.jdbc.Driver"); // 创建数据库连接
Connection connection = DriverManager.getConnection("jdbcUrl", "userName", "password");
// 创建执行语句
Statement statment = connection.createStatement();
// 执行sql,返回结果集
ResultSet resultSet = statement.executeQuery("select * from table;");
// 最后,关闭连接
connection.close();

2. 为啥 Class.forName("com.mysql.jdbc.Driver") 加载一下类就能注册驱动了?

看看 com.mysql.jdbc.Driver 类的代码 (版本5.1.47)

public class Driver  extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}

当类加载的时候会执行静态代码块,从上面可以看到,mysql的Driver静态代码块里边 将自己注册到 DriverManager 里了; 所以后续的 DriverManager#getConnection 的过程能使用到mysql的driver

3. DriverManager 在上述jdbc执行过程中的作用

3.1 DriverManager.registerDriver 显式/主动注册驱动

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();  //驱动列表

public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException { /* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}

registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); 驱动的注册过程就是将其加入到 一个 copyOnWrite 的列表里;

3.2 DriverManager#getConnection 连接创建过程

    for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
} } else {
println(" skipping: " + aDriver.getClass().getName());
}
}

4. DriverManager 自动加载驱动

除了主动调用 DriverManager#registerDriver() 注册驱动外,DriverManager 其实还有两种自动加载驱动的机制:

  • 系统变量 jdbc.drivers 定义的驱动, 然后内部也是通过 Class.forName 去注册这些驱动类
  • 基于 ServiceLoader 的 service-provicer SPI 机制, 即数据库驱动提供方在其类路径下存在文件 /META-INF/services/java.sql.Driver 指定其驱动的实现类,就能被DriverManager自动加载到;

具体逻辑: 在 DriverManager 的静态代码块里,会执行 loadInitialDrivers() 一个自动驱动加载逻辑

 private static void loadInitialDrivers() {
String drivers;
try {
//1. 加载系统变量 jdbc.drivers 设置的冒号隔开的驱动
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
} AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() { // 2. service-provider 加载机制
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
}); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
// 内部也是利用 Class.forName 去加载 jdbc.drivers 定义的Driver类名称
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}

总结

System.getProperty('jdbc.drivers") 这种方式个人觉得适合用于在应用初始化脚本中指定

基于 ServiceLoader 的SPI方式,只要符合 spi 标准的驱动包引入了就会自动加载

我们在基于jdbc实现或封装数据库连接池、中间件的时候,就不需要再 Class.forName 的方式去显示注册驱动了,特别是当需要支持多种数据库的时候,还得根据 jdbcUrl 去判断目标 driverClassName 是啥, 如果要再支持新的关系型数据库时候 还得再改代码

看看常用关系数据库厂商的 spi 支持情况

  • mysql-connector-java 早在5.0.0(2005-12-22) 版本就添加了 META-INF/services/java.sql.Driver 文件支持 service-provicer SPI; 现在一般用8.x的版本
  • pg connector 也在Version 42.2.13(2020-06-04) 之后支持

所以如果不用支持古董版本的驱动,基本可以放心直接 DriverManager#getConnection 去创建db连接了

最新文章

  1. 使用CocosSharp制作一个游戏 - CocosSharp中文教程
  2. rhino(犀牛) --- color control
  3. myeclipse 打开xml jsp页面慢 有时候会自动退出
  4. js中undefined,null,NaN的区别
  5. NYOJ-744蚂蚁的难题(一)
  6. 搭建实时同步data guard的最高可用-切换主备
  7. poj3414(bfs)
  8. Awesome Hadoop
  9. sublime代码格式化插件HTML/CSS/JS prettify
  10. dead loop、continue &amp; break、while...else语句
  11. 016-并发编程-java.util.concurrent.locks之-Lock及ReentrantLock
  12. mybatis.5.动态SQL
  13. python设计模式之装饰器详解(三)
  14. OpenERP对象字段定义的详解
  15. Binder 驱动(三)
  16. SpringBoot如何添加拦截器
  17. BaseAction 类
  18. docker加速器配置
  19. 如何运营亿级QPS的Redis系统
  20. php之变量覆盖漏洞讲解

热门文章

  1. 5、mysql_sql语言介绍
  2. 『动善时』JMeter基础 — 54、JMeter聚合报告详解
  3. mongodb主从复制(读写分离)
  4. tf-gpu报错:ImportError: libcublas.so.10.0: cannot open shared object file: No such file or directory
  5. 大厂高级工程师面试必问系列:Java动态代理机制和实现原理详解
  6. Python报错“UnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe9 in position 0: ordinal not in range(128)”的解决办法
  7. C语言:进制表示
  8. Kubernetes部署-RKE自动化部署
  9. 从新建文件夹开始构建ShadowPlay Engine游戏引擎(4)
  10. 程序员们,还在挣扎着上不了github吗