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连接了

jdbc如何注册数据库驱动Driver的?的更多相关文章

  1. jdbc 加载数据库驱动如何破坏双亲委托模式

    导读      通过jdbc链接数据库,是每个学习Java web 方向的人必然一开始会写的代码,虽然现在各路框架都帮大家封装好了jdbc,但是研究一下jdbc链接的套路还是很意义     术语以及相 ...

  2. JDBC加载数据库驱动的方式

    JDBC作为数据库访问的规范接口,其中只是定义一些接口.具体的实现是由各个数据库厂商来完成. 一.重要的接口: 1.public interface Driver 每个驱动程序类必须实现的接口.Jav ...

  3. JDBC 学习笔记(四)—— JDBC 加载数据库驱动,获取数据库连接

    1. 加载数据库驱动 通常来说,JDBC 使用 Class 类的 forName() 静态方法来加载驱动,需要输入数据库驱动代表的字符串. 例如: 加载 MySQL 驱动: Class.forName ...

  4. jdbc 03:注册驱动的方式

    jdbc连接mysql时,注册驱动的方式 package com.examples.jdbc.o3_注册驱动方式; //mysql驱动所在的包 import com.mysql.jdbc.Driver ...

  5. JDBC链接mysql数据库

    Unit_1 首先:JDBC:java database connectivity SUN公司提供的一套操作数据库的标准规范. JDBC与数据库驱动的关系是接口与实现的关系. JDBC涉及到四个核心的 ...

  6. Java -- JDBC 学习--获取数据库链接

    数据持久化 持久化(persistence): 把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大 ...

  7. 在java程序中使用JDBC连接mysql数据库

    在java程序中我们时常会用到数据库中的数据或操作数据库中的数据,如果java程序没有和我们得数据库连接,就不能实现在java程序中直接操作数据库.使用jdbc就能将java程序和数据库连起来,此时我 ...

  8. Java第三十五天,用JDBC操作MySQL数据库(一),基础入门

    一.JDBC的概念 Java DataBase Connectivity 从字面意思我们也不难理解,就是用Java语言连接数据库的意思 JDBC定义了Java语言操作所有关系型数据库的规则(接口).即 ...

  9. JDBC | 第一章: 快速开始使用JDBC连接Mysql数据库?

    开始使用基于java的JDBC技术来连接mysql进行msyql数据库简单的CRUD操作 下载对应mysql驱动包 这里我创建maven项目基于maven下载 <!--mysql 驱动--> ...

随机推荐

  1. elk 日志收集 filebeat 集群搭建 php业务服务日志 nginx日志 json 7.12版本 ELK 解决方案

    难的不是技术,难的是业务.熟悉业务流程才是最难的. 其实搜索进来的每一个人的需求不一样,希望你能从我的这篇文章里面收获到. 建议还是看官方文档,更全面一些. 一.背景 1,收集nginx  acces ...

  2. Python管道进行数据的吞吐处理

    import multiprocessing import random import time import datetime import struct import os import getF ...

  3. Linux:Linux更新yum方法

    [内容指引]进入目录:cd查看目录下的内容:ls重命名备份:mv从网络下载:wgetyum更新:yum update 第一次运行yum安装软件前,建议更新yum. 1.进入yum源目录 命令: cd ...

  4. <clinit>() 和 <init>()

    原文:https://www.cnblogs.com/aspirant/p/7200523.html <clinit>() 类构造器方法 在 类初始化阶段 被执行 由编译器自动收集类中的所 ...

  5. Java实验项目二——猜数游戏

    1 /* 2 * Description:定义比较类,实现两个数的比较 3 * 4 * */ 5 6 7 package compare; 8 9 import dao.Operate; 10 11 ...

  6. Androidmanifest.xml文件格式详解(转载)

    https://www.jianshu.com/p/eaaae96473f6 来自简书大佬的

  7. Whitzard OJ Introduce to packing

    1.概述 这个就是个smc,为什么会归于加壳,我个人理解是和UPX的运行方式有点像把,不对应该是说和压缩壳的运行方式 很相似,都是先运行一段解密代码,之前的符号表也替换了下 2.解题 有两种方式一种是 ...

  8. 合并两个有序链表---python

    # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # sel ...

  9. Linux之19——Shell编程基础详解

    第一部分:Linux Shell 简介 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序, ...

  10. 7.Java数组

    一.数组概念(最简单的数据结构) 数组是相同类型数据的有序集合. 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成. 其中每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问 ...