jdbc驱动加载过程
使用jdbc创建数据库连接时,业务代码一般需要执行以下代码:
Class.forName("com.mysql.jdbc.Driver");//mysql驱动
Connection conn= DriverManager.getConnection("jdbc:mysql://ip:3306/db","user","pwd");
接下来我们以mysql驱动为例,研究jdbc驱动是如何工作的。
1、驱动加载分析:
1)Class.forName(“com.mysql.jdbc.Driver”):
在早期的jdbc中,在创建连接之前必须执行该代码。当执行Class.forName(“com.mysql.jdbc.Driver”)后,会加载、类初始化(执行static代码)mysql驱动类,代码如下:
package com.mysql.jdbc;
import com.mysql.jdbc.NonRegisteringDriver;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException arg0) {throw new RuntimeException("Can\'t register driver!");}}
}
生成驱动实例对象,并向DriverManager注册(将实例添加到list中),代码如下:
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {if(driver != null) {registeredDrivers.addIfAbsent(new DriverInfo(driver));} else {throw new NullPointerException();}
}
2)DriverManager类:
调用DriverManager.registerDriver()方法之前肯定得先初始化DriverManager类,代码如下:
static {loadInitialDrivers();println("JDBC DriverManager initialized");
}
loadInitialDrivers方法会通过两种方式加载驱动实现类:
- 使用Class.forName加载jdbc.drivers系统变量中指定的驱动实现类;
- 使用SPI加载驱动实现类;
private static void loadInitialDrivers() {String drivers;try {//1、获取系统变量中的驱动实现类名,然后通过下面的Class.forName加载drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("jdbc.drivers");}});} catch (Exception ex) {drivers = null;}//2、使用SPI获取驱动实现类AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}});//通过Class.forName加载jdbc.drivers系统变量中指定的驱动类if (drivers != null && !drivers.equals("")) {String[] driversList = drivers.split(":");println("number of Drivers:" + driversList.length);for (String aDriver : driversList) {try {Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());} catch (Exception ex) {}}}
}
注意:如果配置了jdbc.drivers变量,使用Class.forName加载驱动类时需要传入系统ClassLoader,因为DriverManager类是boot类加载器加载的,如果直接调用Class.forName(aDriver),就会使用boot类加载器来加载驱动,显然是加载不到的。
说明:
- 由于高版本jdk中的jdbc使用了SPI加载驱动实现类,所以不需要在前面显示的通过Class.forName("com.mysql.jdbc.Driver");来加载驱动类了。(写了也不会出错)
- 在以前的低版本中如果不写Class.forName("com.mysql.jdbc.Driver");,就需要设置jdbc.drivers系统变量,二者的用途是一样的,都是使用Class.forName方法来加载指定的驱动类;(驱动类一旦加载会执行其static代码,将驱动实例对象注册到DriverManager中)
3)SPI之ServiceLoader.load():
在ServiceLoader.load时,根据传入的接口类,遍历META-INF/services目录下的以该类命名的文件中的所有类,并实例化返回,同样当加载驱动类时,会调用驱动类的static代码,将其注册到jdbc的DriverManager中。ServiceLoader是spi机制的一个实现,具体见:https://blog.csdn.net/liuxiao723846/article/details/112500485
通过SPI加载、实例化这些驱动程序,需要注意一点:
可能是因为驱动程序类可能不存在,即可能有带有服务类别的打包驱动程序作为java.sql.Driver的实现,但实际的类可能会丢失。在这种情况下,java.util.ServiceConfigurationError会在运行时由VM试图定位时抛出并加载服务。所以,这里使用try catch块来捕获那些运行时错误,如果驱动程序在classpath中不可用,但是打包为服务,并且该服务在classpath中。
2、获取驱动连接分析:
1)getConnection():
@CallerSensitive
public static Connection getConnection(String url,String user,String password) throws SQLException {java.util.Properties info = new java.util.Properties();if (user != null) info.put("user", user);if (password != null) info.put("password", password);return (getConnection(url, info, Reflection.getCallerClass()));
}
将调用类的classLoader传给了getConnection方法(调用类是我们的业务代码,所以其类加载器就是系统类加载器),目的是后面再去加载数据库的driver,做一下验证(isDriverAllowed方法)。
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {//caller为空,从线程上下文获取(默认是系统类加载器)ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;synchronized (DriverManager.class) {if (callerCL == null) {callerCL = Thread.currentThread().getContextClassLoader();}}SQLException reason = null;for(DriverInfo aDriver : registeredDrivers) {//从注册的list中获取驱动示例// 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!return (con);}} catch (SQLException ex) {if (reason == null) reason = ex;}} else {println(" skipping: " + aDriver.getClass().getName());}}if (reason != null) {// if we got here nobody could connect.println("getConnection failed: " + reason);throw reason;}throw new SQLException("No suitable driver found for "+ url, "08001");
}
说明:getConnection方法就是从registerdDrivers(是个list)中遍历所有的驱动实现类,驱动内部会根据url协议来判断是否该创建对应的数据库连接;如果所有驱动都无法解析url创建连接,则抛出异常。
2)isDriverAllowed()方法:
该方法是通过“调用类的类加载器”(一般来说就是系统类加载器)再初始化一次驱动类,其目的是为了确保事先注册的驱动与当前的驱动是通过同一个类加载器加载的。
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {boolean result = false;if(driver != null) {Class<?> aClass = null;try {aClass = Class.forName(driver.getClass().getName(), true, classLoader);} catch (Exception ex) {result = false;}result = ( aClass == driver.getClass() ) ? true : false;}return result;
}
总结:
使用jdbc创建具体数据库驱动库的连接,主要思路是:加载驱动类,在驱动类的static代码中按照jdbc规范,将其实例注册到DriverManager中,在创建连接时遍历注册的list,先进行驱动实例的校验,然后根据url协议创建出连接。加载驱动类的方式有:
- 设置jdbc.drivers的系统变量;
- 使用Class.forName指定驱动类;
- 使用SPI加载驱动类;
参考:
https://www.cnblogs.com/webor2006/p/9275901.html
https://www.cnblogs.com/silyvin/p/12192617.html
jdbc驱动加载过程相关推荐
- JDBC驱动加载机制详解以及spi机制
首先有两个问题: 1.java连接数据库时是否真的需要加载驱动? 2.JDBC如何区分多个驱动? 以下摘自:https://blog.csdn.net/buqutianya/article/detai ...
- java加载jdbc驱动,加载JDBC驱动
我们平时在连接数据库时需要加载驱动,通常做法是将JDBC驱动程序放在类路径中的某个位置,然后用Class.forName()查找并加载驱动程序. 这也就意味着要么将驱动程序打包到jar中,要么将驱动程 ...
- spring boot中mysql驱动加载过程
1.我们一般选择的Datasoure工具类 private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { "o ...
- linux内核驱动识别过程,转载_ARM-Linux内核驱动加载过程思路
Ø 取得驱动C文件并建立相应文件夹实现内核添加(以编译测试驱动程序为例) 1) 在相应的文件夹下面建立驱动文件夹 将C程序放入该驱动文件夹下 2) 创建或下载驱动C文 ...
- JDBC中驱动加载的过程分析
JDBC中驱动加载的过程分析 作者:kenty 来源:博客园 发布时间:2007-08-20 15:01 阅读:1100 次 原文链接 [收藏] 本篇从java.sql.Driver ...
- HarmonyOS镜像,HarmonyOS驱动加载过程分析
HDF(硬件驱动程序基础)驱动程序框架为驱动器开发人员提供驱动程序框架功能,包括驱动器加载,驱动服务管理和驱动程序消息传递机制.它旨在构建一个统一的驱动器架构平台,为驾驶开发人员提供更准确,更高效的开 ...
- 老调重弹:JDBC系列之驱动加载原理全面解析)
前言 最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读 ...
- 老调重弹:JDBC系列 之 驱动加载原理全面解析
前言 最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读 ...
- 非即插即用型设备驱动的加载过程
非即插即用型设备驱动的加载过程 1. 非PnP总线驱动在系统启动时通过扫描注册表发现非PnP设备的存在,并向OS报告ID信息.(例如根总线驱动通过扫描 HKLM\ SYSTEM\ CurrentCon ...
- 设备树语法,加载过程和与驱动的关系
文章目录 一.设备树语法 1.1 简介 1.2 基本数据格式 1.3 一个例子 1.3.1 根节点 2.3.2 CPU 1.3.3 节点名称 1.3.4 设备 1.3.5 status 1.3.6 编 ...
最新文章
- Apache安装遇到的相关问题
- 使用 Git Extensions 简单入门 Git
- 《Effective Objective-C 2.0》1、熟悉Objective-C
- Aptana Studio 3 如何汉化,实现简体中文版
- 跟我学android-Android应用基本组件介绍(五)
- 人体轮廓_速写人体轮廓的处理有哪些技法?
- 毕业设计-基于stm32的校园旧物回收系统
- 二维函数Z=g(X,Y)型,用卷积公式求概率密度,积分区域如何确定(上)
- 用python将文件夹里的图片统一修改名字
- RNA结构预测竞赛圆满落幕,优胜选手花式解题思路齐奉上
- 文华财经多个非常实用的期货指标公式,文华财经支撑压力自动画线公式
- 基于图神经网络的知识图谱研究进展
- 【高级软件工程课后作业】
- 微信小程序应该这样开发
- VMware Workstation 虚拟机不兼容解决方法
- win10双 cd linux,win10系统将CdLinux装入硬盘的还原方案
- win10 wmware 花屏_用了N年浑不知!原来Win10竟有这么多隐藏功能
- STM32F407VET6的OTM8009_800X480屏幕驱动移植
- 常见无法登陆MSN原因
- 12、【斯纳克图书馆管理系统】 挂失管理
热门文章
- jad反编译成java,反编译工具jad的使用(将*.class文件变成*.java文件,附带jad.zip包)...
- 梦幻古龙 服务器名称修改,【鬼族大话西游】(梦幻古龙改版)虚拟机镜像服务端+配套客户端+GM工具+启动教程...
- 地质专业考遥感计算机研究生,我想考中国地质大学的研究生,谁能告诉我是选遥感..._在职考研_帮考网...
- SPSS实现重复测量方差分析
- 2012考研英语--前辈的高分复习经验
- 手机怎么用外嵌字幕_教你用手机给视频添加字幕,超简单,3分钟就能学会
- 【密码学】抽象代数——群(学习笔记)
- python实现空气焓值,湿球温度的计算
- Johnson 算法
- web前端常用开发工具有哪些?