博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
老调重弹:JDBC系列 之 <驱动载入原理全面解析>
阅读量:7074 次
发布时间:2019-06-28

本文共 9795 字,大约阅读时间需要 32 分钟。

前言

         近期在研究Mybatis框架,因为该框架基于JDBC。想要非常好地理解和学习Mybatis,必需要对JDBC有较深入的了解。所以便把JDBC 这个东东翻出来。好好总结一番,作为自己的笔记,也是给读者一个參考~~~下面是本文的组织结构。读者能够点击上面的文件夹查看:

概述       

      普通情况下。在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现载入到系统内存中。然后供系统使用。

基本结构图例如以下:

   

驱动载入入内存的过程

    这里所谓的驱动,事实上就是实现了java.sql.Driver接口的类。如oracle的驱动类是 oracle.jdbc.driver.OracleDriver.class(此类能够在oracle提供的JDBC jar包中找到),此类实现了java.sql.Driver接口。

因为驱动本质上还是一个class,将驱动载入到内存和载入普通的class原理是一样的:使用Class.forName("driverName")。下面是将经常使用的数据库驱动载入到内存中的代码:

//载入Oracle数据库驱动			Class.forName("oracle.jdbc.driver.OracleDriver");						//载入SQL Server数据库驱动			Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");						//载入MySQL 数据库驱动			Class.forName("com.mysql.jdbc.Driver");

         注意:Class.forName()将相应的驱动类载入到内存中,然后运行内存中的static静态代码段,代码段中。会创建一个驱动Driver的实例,放入DriverManager中,供DriverManager使用。

      比如,在使用Class.forName() 载入oracle的驱动oracle.jdbc.driver.OracleDriver时。会运行OracleDriver中的静态代码段,创建一个OracleDriver实例。然后调用DriverManager.registerDriver()注冊:

static {		Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");		try {			if (defaultDriver == null) {				//创建一个OracleDriver实例,然后注冊到DriverManager中                                defaultDriver = new OracleDriver();				DriverManager.registerDriver(defaultDriver);			}		} catch (RuntimeException localRuntimeException) {		} catch (SQLException localSQLException) {		}

Driver的功能

      java.sql.Driver接口规定了Driver应该具有下面功能:

当中:

acceptsURL(String url) 方法用来測试对指定的url,该驱动是否能打开这个url连接。driver对自己可以连接的url会制定自己的协议。仅仅有符合自己的协议形式的url才觉得自己可以打开这个url,假设可以打开。返回true,反之,返回false;

比如:oracle定义的自己的url协议例如以下:

jdbc:oracle:thin:@//<host>:<port>/ServiceName

jdbc:oracle:thin:@<host>:<port>:<SID>

oracle自己的acceptsURL(String url)方法例如以下:

public boolean acceptsURL(String paramString) {		if (paramString.startsWith("jdbc:oracle:")) {			return (oracleDriverExtensionTypeFromURL(paramString) > -2);		}		return false;	}	private int oracleDriverExtensionTypeFromURL(String paramString) {		int i = paramString.indexOf(58) + 1;		if (i == 0) {			return -2;		}		int j = paramString.indexOf(58, i);		if (j == -1) {			return -2;		}		if (!(paramString.regionMatches(true, i, "oracle", 0, j - i))) {			return -2;		}		++j;		int k = paramString.indexOf(58, j);		if (k == -1) {			return -3;		}		String str = paramString.substring(j, k);		if (str.equals("thin")) {			return 0;		}		if ((str.equals("oci8")) || (str.equals("oci"))) {			return 2;		}		return -3;	}

     由上可知oracle定义了自己应该接收什么类型的URL。自己能打开什么类型的URL连接(注意:这里acceptsURL(url)仅仅会校验url是否符合协议,不会尝试连接推断url是否有效) 。拓展阅读:

connect(String url,Properties info)方法。创建Connection对象,用来和数据库的数据操作和交互,而Connection则是真正数据库操作的開始(在此方法中。没有规定是否要进行acceptsURL()进行校验)。

手动载入驱动 Driver 并实例化进行数据库操作的样例

public static void driverTest(){		try {			//1.载入oracle驱动类,并实例化			Driver driver = (Driver) Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();			//2.判定指定的URL oracle驱动是否能接受(符合oracle协议规则)			boolean flag = driver.acceptsURL("jdbc:oracle:thin:@127.0.0.1:1521:xe");			//标准协议測试			boolean standardFlag1 = driver.acceptsURL("jdbc:oracle:thin:@//
:
/ServiceName"); boolean standardFlag2 = driver.acceptsURL("jdbc:oracle:thin:@
:
:
"); System.out.println("协议測试:"+flag+"\t"+standardFlag1+"\t"+standardFlag2); //3.创建真实的数据库连接: String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe"; Properties props = new Properties(); props.put("user", "louluan"); props.put("password", "123456"); Connection connection = driver.connect(url, props); //connection 对象用于数据库交互。代码省略。

。。。。

} catch (Exception e) { System.out.println("载入Oracle类失败!"); e.printStackTrace(); } finally{ } }

  

       上述的手动载入Driver而且获取连接的过程稍显笨拙:假设如今我们载入进来了多个驱动Driver,那么手动创建Driver实例。并依据URL进行创建连接就会显得代码杂乱无章。而且还easy出错。而且不方便管理。JDBC中提供了一个DriverManager角色,用来管理这些驱动Driver。

DriverManager角色

         其实。一般我们操作Driver。获取Connection对象都是交给DriverManager统一管理的。

DriverManger能够注冊和删除载入的驱动程序,能够依据给定的url获取符合url协议的驱动Driver或者是建立Conenction连接,进行数据库交互。

 

    下面是DriverManager的关键方法摘要:

   

    DriverManager 内部持有这些注冊进来的驱动 Driver,因为这些驱动都是 java.sql.Driver 类型,那么如何才干获得指定厂商的驱动Driver呢?答案就在于:

      java.sql.Driver接口规定了厂商实现该接口,而且定义自己的URL协议。厂商们实现的Driver接口通过acceptsURL(String url)来推断此url是否符合自己的协议。假设符合自己的协议,则能够使用本驱动进行数据库连接操作,查询驱动程序是否觉得它能够打开到给定 URL 的连接。

使用DriverManager获取指定Driver

     对于驱动载入后,怎样获取指定的驱动程序呢?这里,DriverManager的静态方法getDriver(String url)能够通过传递给的URL,返回能够打开此URL连接的Driver。

     比方。我想获取oracle的数据库驱动。仅仅须要传递形如jdbc:oracle:thin:@<host>:<port>:<SID>或者jdbc:oracle:thin:@//<host>:<port>/ServiceName的參数给DriverManager.getDriver(String url)就可以:

Driver oracleDriver =DriverManager.getDriver("jdbc:oracle:thin:@
:
:
");

     实际上。DriverManager.getDriver(String url)方法是依据传递过来的URL。遍历它维护的驱动Driver。依次调用驱动的Driver的acceptsURL(url)。假设返回acceptsURL(url)返回true。则返回相应的Driver:

public static Driver getDriver(String paramString) throws SQLException {			//省略部分代码。。。。		Iterator localIterator = registeredDrivers.iterator();		//遍历注冊的驱动		while (localIterator.hasNext()) {			DriverInfo localDriverInfo = (DriverInfo) localIterator.next();			if (isDriverAllowed(localDriverInfo.driver, localClass))				try {					//假设accepsURL() 为true,返回相应的driver					if (localDriverInfo.driver.acceptsURL(paramString)) {						//返回相应的driver						return localDriverInfo.driver;					}				} catch (SQLException localSQLException) {				}			else				println("    skipping: "+ localDriverInfo.driver.getClass().getName());		}		throw new SQLException("No suitable driver", "08001");		//-----省略部分代码	}

使用DriverManager注冊和取消注冊驱动Driver

      在本博文開始的 驱动载入的过程一节中,讨论了当使用Class.forName("driverName")载入驱动的时候。会向DriverManager中注冊一个Driver实例。下面代码将验证此说法:

public static void defaultDriver(){		try {						String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";						//1.将Driver载入到内存中,然后运行其static静态代码。创建一个OracleDriver实例注冊到DriverManager中			Class.forName("oracle.jdbc.driver.OracleDriver");			//取出相应的oracle 驱动Driver			Driver driver  = DriverManager.getDriver(url);			System.out.println("载入类后,获取Driver对象:"+driver);						//将driver从DriverManager中注销掉			DriverManager.deregisterDriver(driver);			//又一次通过url从DriverManager中取Driver			driver  = DriverManager.getDriver(url);			System.out.println(driver);					} catch (Exception e) {			System.out.println("载入Oracle类失败。");			e.printStackTrace();		} finally{					}	}
以上代码主要分下面几步:

1. 首先是将  oracle.jdbc.driver.OracleDriver载入到内存中。

 2.  然后便调用DriverManager.getDriver()去取Driver实例;

3.  将driver实例从DriverManager中注销掉;

4.尝试再取 相应url的Driver实例;

上述代码运行的结果例如以下:

      从运行结果看,正好可以验证以上论述:当第四步再次获取相应url的 Driver 实例时。因为已经被注销掉了,找不到适当的驱动Driver。抛出了 "Not suitable driver" 的异常。

     将上述的样例稍作变化,在注销掉了静态块创建的driver后,往DriverManager注冊一个自己创建的Driver对象实例(详细步骤请看凝视):

public static void defaultDriver(){		try {						String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";						//1.将Driver载入到内存中,然后执行其static静态代码。创建一个OracleDriver实例注冊到DriverManager中			Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();			//2.取出相应的oracle 驱动Driver			Driver driver  = DriverManager.getDriver(url);			System.out.println("载入类后,获取Driver对象:"+driver);						//3. 将driver从DriverManager中注销掉			DriverManager.deregisterDriver(driver);						//4.此时DriverManager中已经没有了驱动Driver实例,将创建的dd注冊到DriverManager中			DriverManager.registerDriver(dd);						//5.又一次通过url从DriverManager中取Driver			driver  = DriverManager.getDriver(url);                                               System.out.println("注销掉静态创建的Driver后,又一次注冊的Driver:    "+driver);			System.out.println("driver和dd是否是同一对象:" +(driver==dd));		} catch (Exception e) {			System.out.println("载入Oracle类失败!

"); e.printStackTrace(); } finally{ } }

下面代码执行的结果:

  以上代码先创建了一个Driver对象,在注销了DriverManager中由载入驱动过程中静态创建驱动之后。注冊到系统中。如今DriverManager中相应url返回的Driver 即是在代码中创建的Driver对象。

使用DriverManager创建 Connection 连接对象

      创建 Connection 连接对象。能够使用驱动Driver的 connect(url,props)。也能够使用 DriverManager 提供的getConnection()方法。此方法通过url自己主动匹配相应的驱动Driver实例,然后调用相应的connect方法返回Connection对象实例。

Driver driver  = DriverManager.getDriver(url);			Connection connection = driver.connect(url, props);
上述代码等价于:
Class.forName("oracle.jdbc.driver.OracleDriver");			Connection connection = DriverManager.getConnection(url, props);

jdbc.drivers

      DriverManager 作为 Driver 的管理器,它在第一次被使用的过程中(即在代码中第一次用到的时候)。它会被载入到内存中。然后运行其定义的static静态代码段。在静态代码段中,有一个  loadInitialDrivers()  静态方法,用于载入配置在jdbc.drivers 系统属性内的驱动Driver。配置在jdbc.drivers 中的驱动driver将会首先被载入:

static {		loadInitialDrivers();//载入配置在jdbc.drivers系统变量中的驱动driver		println("JDBC DriverManager initialized");		SET_LOG_PERMISSION = new SQLPermission("setLog");	}
private static void loadInitialDrivers() {		String str1;		try {			str1 = (String) AccessController					.doPrivileged(new PrivilegedAction() {						public String run() {							return System.getProperty("jdbc.drivers");//返回jdbc.drivers值						}					});		} catch (Exception localException1) {			str1 = null;		}                //省略部分代码......		if ((str1 == null) || (str1.equals("")))			return;		String[] arrayOfString1 = str1.split(":");		println("number of Drivers:" + arrayOfString1.length);		for (String str2 : arrayOfString1)			try {				//Class.forName载入相应的driver				Class.forName(str2, true, ClassLoader.getSystemClassLoader());			} catch (Exception localException2) {				println("DriverManager.Initialize: load failed: "						+ localException2);			}	}

下面是通过jdbc.drivers载入驱动driver的方式:

String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";			//设置值系统变量jdbc.drivers			System.setProperty("jdbc.drivers", "oracle.jdbc.driver.OracleDriver");			//2.通过特定的url获取driver			Driver driver = DriverManager.getDriver(url);			//打印是否存在			System.out.println(driver);
请记住:一定要在第一次使用DriverManager之前设置jdbc.drivers,由于DriverManager中的static静态代码段仅仅会被运行
一次。

-------------------------------------------------------------------------------------------------------------------------------------------------------------    

        以上是本文 老调重弹:JDBC系列 之 <驱动载入原理全面解析>的所有内容,以上是自己心得。并不是权威。如有不妥或者此错误之处,欢迎读者批评和斧正!  欢迎关注我的下一篇博文: 老调重弹:JDBC系列 之 <JDBC层次结构和基本构成>

你可能感兴趣的文章
关于技术
查看>>
adb学习2
查看>>
面试之STAR法则详解
查看>>
inno安装
查看>>
禁用缓存的设置
查看>>
Idea14解决JSP/JS文件需要重启问题解决
查看>>
对IoC DI的理解
查看>>
tactic remove project
查看>>
实现汉字转拼音
查看>>
太吃鸡了
查看>>
Smart2.0开发指南——开发工具
查看>>
oracle——06表查询中需要注意的一些问题
查看>>
大白话讲Zookeeper能做什么?(一):命名服务与配置管理
查看>>
java httpclient使用socks5代理(二)使用socks5代理服务
查看>>
java实现多线程的三种方式
查看>>
汇编 输入输出字符串(最简单版)
查看>>
Submit a form with Ajax 发送邮件参考
查看>>
国外PHP和MySQL数据库代码范例分享和指南站点
查看>>
Suse Linux服务器系统下安装运行HDF相关程序
查看>>
从头编写一个 HTTP 静态资源服务器
查看>>