您的位置:首页 > 博客中心 > 数据库 >

老调重弹:JDBC系列 之 <驱动加载原理全面解析>

时间:2022-03-10 17:36



//加载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) {
		}



其中:

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()进行校验)。

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:@//<host>:<port>/ServiceName"); boolean standardFlag2 = driver.acceptsURL("jdbc:oracle:thin:@<host>:<port>:<SID>"); 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进行创建连接就会显得代码杂乱无章,并且还容易出错,并且不方便管理。JDBC中提供了一个DriverManager角色,用来管理这些驱动Driver。

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

    gxlsystem.com,布布扣

    DriverManager 内部持有这些注册进来的驱动 Driver,由于这些驱动都是 java.sql.Driver 类型,那么怎样才能获得指定厂商的驱动Driver呢?答案就在于:

      java.sql.Driver接口规定了厂商实现该接口,并且定义自己的URL协议。厂商们实现的Driver接口通过acceptsURL(String url)来判断此url是否符合自己的协议,如果符合自己的协议,则可以使用本驱动进行数据库连接操作,查询驱动程序是否认为它可以打开到给定 URL 的连接。

Driver oracleDriver =DriverManager.getDriver("jdbc:oracle:thin:@<host>:<port>:<SID>");

     实际上,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");
		//-----省略部分代码
	}

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实例;

上述代码执行的结果如下:

gxlsystem.com,布布扣

      从执行结果看,正好能够验证以上论述:当第四步再次获取对应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{
			
		}
	}
以下代码运行的结果:

gxlsystem.com,布布扣

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

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

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层次结构和基本构成>




老调重弹:JDBC系列 之 <驱动加载原理全面解析>,布布扣,bubuko.com

热门排行

今日推荐

热门手游