java数据库连接驱动分析

来源:互联网 发布:锤子坚果pro2 知乎 编辑:程序博客网 时间:2024/05/02 15:51

注: 转自http://blog.csdn.net/brilliancezhou/article/details/5425655

    JAVA连接数据库是其众多功能中的一部分,主要有两种方式连接DataBase: 一种是采用JDBC-ODBC桥,另一种则是称之为纯驱动连接DataBase,第一种方式在大型项目中基本上不再使用,本系列文章主要分析纯驱动源码。
对于初学JAVA者,甚至那些使用JAVA做过几年开发的程序员来讲,对于JDBC的工作原理都不一定能够明白。知其然,不知其所以然。遇到问题就不知所措了。通过针对于MYSQL JDBC源码的分析,对于JAVA是如何连接数据库,其中到底做了些什么工作,一步步解剖开来,更好的理解JDBC。
使用JAVA连接数据库,首先要做的就是在程序中导入sql包,然后装载驱动类、获取连接、获取语句对象、发送SQL命令然后得到结果
请看以下代码片段:

[java] view plaincopy
  1. /* 连接mysql 时装载的驱动类以及连接字符串 */  
  2. Class.forName(“com.mysql.jdbc.Driver”);//1  
  3. DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123”);//2  
  4. /* 连接SQLServer2005 时装载的驱动类以及连接字符串 */  
  5. Class.forName(“com.microsoft.sqlserver.jdbc.SQLServerDriver”);  
  6. DriverManager.getConnection(“jdbc:sqlserver://localhost:1433;databaseName=pubs”,”sa”, ””);  

此段代码有两部分,连接不同数据库时所需要装载的驱动类以及连接字符串,以此获取连接。
Class.forName()装载类,在调用一个类的构造方法,初始化静态成员或者这个类有main方法时,JVM都会装载对应的类。
首先我们就看看com.mysql.jdbc.Driver类做了什么事情,找到MYSQL-JDBC驱动源码,解压之后找到src目录,然后找到com.mysql.jdbc下的Driver.java类

[java] view plaincopy
  1. package com.mysql.jdbc;  
  2. import java.sql.SQLException;  
  3. public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
  4.     static {  
  5.         try {  
  6.             java.sql.DriverManager.registerDriver(new Driver()); //1  
  7.         } catch (SQLException E) {  
  8.             throw new RuntimeException("Can't register driver!");  
  9.         }  
  10.     }  
  11.     // ~ Constructors  
  12.     // -----------------------------------------------------------  
  13.     /** 
  14.      * Construct a new driver and register it with DriverManager 
  15.      *  
  16.      * @throws SQLException 
  17.      *             if a database error occurs. 
  18.      */  
  19.     public Driver() throws SQLException {  
  20.         // Required for Class.forName().newInstance()  
  21.     }  
  22. }  

Driver类继承NonRegisteringDriver 同时实现接口java.sql.Driver
此类会有一个静态块

[java] view plaincopy
  1. static {  
  2.         try {  
  3.             java.sql.DriverManager.registerDriver(new Driver()); //1  
  4.         } catch (SQLException E) {  
  5.             throw new RuntimeException("Can't register driver!");  
  6.         }  
  7.     }  

Class.forName的作用是要求JVM查找并加载指定的类, 也就是说JVM装载此类并执行静态块代码
此静态块只有一句关键部分,1处
在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBC 驱动
的Driver类的代码都必须类似下面这段
java.sql.DriverManager.registerDriver(new Driver());
所以,如果你要自己实现一个数据库的JDBC驱动,那么就得实现java.sql.Driver接口,并且需要在实现类中使用java.sql.DriverManager.registerDriver(new Driver())注册自己,new Driver()就是创建一个Driver对象,所以此类会有一个无参数的构造函数:

[java] view plaincopy
  1. public Driver() throws SQLException {  
  2.  }  

下面再看看DriverManager.registerDriver()这个方法,源码如下:

[java] view plaincopy
  1. public static synchronized void registerDriver(java.sql.Driver driver)  
  2.     throws SQLException {  
  3.     if (!initialized) {  
  4.         initialize();  
  5.     }  
  6.         
  7.     DriverInfo di = new DriverInfo();  
  8.     di.driver = driver;  
  9.     di.driverClass = driver.getClass();  
  10.     di.driverClassName = di.driverClass.getName();  
  11.     // Not Required -- drivers.addElement(di);  
  12.     writeDrivers.addElement(di);   
  13.     println("registerDriver: " + di);  
  14.       
  15.     /* update the read copy of drivers vector */  
  16.     readDrivers = (java.util.Vector) writeDrivers.clone();  
  17. }  


此方法是一个静态同步的方法,形式参数是java.sql.Driver接口类型,因为com.mysql.jdbc.Driver这个类实现了java.sql.Driver接口,所以com.mysql.jdbc.Driver实例对象new Driver()是可以作为实参传入到此方法中来的。在DriverManager类中都是使用的Driver接口类型,也就是说驱动的使用不依赖于任何实现。如果需要更换你所连接的数据库,只需要在Class.forName传入的参数换成另一个数据库的驱动类,但要求此类必须实现Driver接口。

上一篇中分析了Class.forName(“com.mysql.jdbc.Driver”)幕后所做的事情,也就是在Driver实现类中的静态块和构造函数,本篇主要来分析一下静态块的一句代码:DriverManager.registerDriver方法和其它相关的调用。
    registerDriver方法是一个静态方法,它所要做的工作就是加载所有系统提供的驱动,并把它们添加到具体的类中,形成对象。同时还创建连接,是一个管理驱动的工具类。如果我们使用的是mysql,那么加载的也就是它的驱动。
    此方法的源码如下:

[java] view plaincopy
  1. public static synchronized void registerDriver(java.sql.Driver driver)  
  2.     throws SQLException {  
  3.     if (!initialized) {  //1  
  4.         initialize();  
  5.     }        
  6.     DriverInfo di = new DriverInfo();  
  7.     di.driver = driver;  
  8.     di.driverClass = driver.getClass();  
  9.     di.driverClassName = di.driverClass.getName();  
  10.     // Not Required -- drivers.addElement(di);  
  11.     writeDrivers.addElement(di);   
  12.     println("registerDriver: " + di);  
  13.     /* update the read copy of drivers vector */  
  14.     readDrivers = (java.util.Vector) writeDrivers.clone();  
  15. }  

一、初始化操作
1、看看1处的代码,判断是否初始化,这个判断的变量是一个静态全局boolean值,初始为false
    private static boolean initialized = false;


如果此变量的值为false那么它将会进入初始化方法,源码如下:

[java] view plaincopy
  1. static void initialize() {  
  2.         if (initialized) {  
  3.             return;  
  4.         }  
  5.         initialized = true;  
  6.         loadInitialDrivers();  
  7.         println("JDBC DriverManager initialized");  
  8.     }  

2、Initialize方法中判断initialized值是否为真(其实就是通过此boolean变量判断是否已经初始化完成),之后设置initialized值为true,接着又会调用另一个方法loadInitialDrivers() 同样是静态方法,用于调用系统类装载器,装载所有系统提供的驱动:
loadInitialDrivers()源码:

[java] view plaincopy
  1. private static void loadInitialDrivers() {  
  2.         String drivers;  
  3.       
  4.         try {  
  5.         drivers = (String) java.security.AccessController.doPrivileged(  
  6.         new sun.security.action.GetPropertyAction("jdbc.drivers"));  
  7.         } catch (Exception ex) {  
  8.             drivers = null;  
  9.         }  
  10.           
  11.         // If the driver is packaged as a Service Provider,  
  12.         // load it.  
  13.         // Get all the drivers through the classloader   
  14.         // exposed as a java.sql.Driver.class service.  
  15.       
  16.      DriverService ds = new DriverService();  
  17.      // Have all the privileges to get all the   
  18.      // implementation of java.sql.Driver  
  19.      java.security.AccessController.doPrivileged(ds);  
  20.          println("DriverManager.initialize: jdbc.drivers = " + drivers);  
  21.         if (drivers == null) {  
  22.             return;  
  23.         }  
  24.         while (drivers.length() != 0) {  
  25.             int x = drivers.indexOf(':');  
  26.             String driver;  
  27.             if (x < 0) {  
  28.                 driver = drivers;  
  29.                 drivers = "";  
  30.             } else {  
  31.                 driver = drivers.substring(0, x);  
  32.                 drivers = drivers.substring(x+1);  
  33.             }  
  34.             if (driver.length() == 0) {  
  35.                 continue;  
  36.             }  
  37.             try {  
  38.                 println("DriverManager.Initialize: loading " + driver);  
  39.                 Class.forName(driver, true,  
  40.                   ClassLoader.getSystemClassLoader());  
  41.             } catch (Exception ex) {  
  42.                 println("DriverManager.Initialize: load failed: " + ex);  
  43.             }  
  44.         }  
  45.     }  

主要代码分析:
    下面这段创建了一个内部类对象,创建此对象时,它会从系统服务中加载驱动

[java] view plaincopy
  1. DriverService ds = new DriverService();  

DriverService内部类的具体代码:

[java] view plaincopy
  1. class DriverService implements java.security.PrivilegedAction {  
  2.         Iterator ps = null;  
  3.     public DriverService() {};  
  4.         public Object run() {  
  5.     ps = Service.providers(java.sql.Driver.class);  //从系统服务中加载驱动  
  6.     try {  
  7.            while (ps.hasNext()) { //遍历驱动  
  8.                ps.next();  
  9.            } // end while  
  10.     } catch(Throwable t) {  
  11.         // Do nothing  
  12.     }  
  13.         return null;  
  14.     } //end run  
  15. //end DriverService  

 

此句代码就是找到所有的拥有权限的java.sql.Driver的实现

[java] view plaincopy
  1. java.security.AccessController.doPrivileged(ds);  

下面这段,意思是得到系统属性jdbc.drivers对应驱动的驱动名称,使用了JAVA的安全许可

[java] view plaincopy
  1. drivers = (String) java.security.AccessController.doPrivileged(  
  2.         new sun.security.action.GetPropertyAction("jdbc.drivers"));  

再看看后面的判断和循环
首先判断驱动服务对象是否为null,如果为null则返回,否则进入while循环,这个循环会依次遍历多个数据库驱动,因为jdbc:drivers会有多个数据库驱动,驱动名是以:分割,接下来就是通过Class.forName依次装载驱动类,在其中使用了ClassLoader.getSystemClassLoader()系统类装载器。

[java] view plaincopy
  1. if (drivers == null) {  
  2.             return;  
  3.         }  
  4.  while (drivers.length() != 0) {  
  5. …  
  6. Class.forName(driver, true, ClassLoader.getSystemClassLoader());  
  7. …  
  8. }  

上面分析的就是在registerDriver方法中所要做的第一件事情:初始化。可以看到initialize()做的工作就是装载驱动,同时还需要使用到系统的一些功能。如: java.security.AccessController.doPrivileged,此方法允许在一个类实例中的代码通知这个AccessController,它的代码主体享受特权(Privileged),它不管这个请求是由什么代码所引发的,只是单独负责对它可得到的资源的访问请求。比如说,一个调用者在调用doPrivileged方法时,可被标识为特权。AccessController做访问控制决策时,如果checkPermission方法遇到一个通过doPrivileged方法调用而被视为特权调用者,那么checkPermission方法不会作许可检查,表示那个访问请求是被允许的,如果调用者没有许可,则会抛出一个异常。

如:ClassLoader.getSystemClassLoader(),java中所有类都是通过ClassLoader装载的,ClassLoader可以为java程序提供很好的动态特性,有必要去深入理解哦。

接下来再看初始化之后的代码:

[java] view plaincopy
  1. DriverInfo di = new DriverInfo();  
  2.     di.driver = driver;  
  3.     di.driverClass = driver.getClass();  
  4.     di.driverClassName = di.driverClass.getName();  
  5.     // Not Required -- drivers.addElement(di);  
  6.     writeDrivers.addElement(di);   
  7.     println("registerDriver: " + di);  
  8.     /* update the read copy of drivers vector */  
  9.     readDrivers = (java.util.Vector) writeDrivers.clone();  

创建DriverInfo对象
DriverInfo di = new DriverInfo();
DriverInfo驱动信息类,是一个内部类,
源码如下:

[java] view plaincopy
  1. class DriverInfo {  
  2.     Driver         driver;  
  3.     Class          driverClass;  
  4.     String         driverClassName;  
  5.     public String toString() {  
  6.     return ("driver[className=" + driverClassName + "," + driver + "]");  
  7.     }  
  8. }  

此类就是添加了三个属性,分别表示驱动对象,驱动的Class对象,以及驱动的类名;同时重写了toString方法。此内部类的作用就是以可以创建DriverInfo对象,以对象的形式保存驱动信息。

接下来就是设置对象的三个属性:

[java] view plaincopy
  1. DriverInfo di = new DriverInfo();  
  2. di.driver = driver;  
  3. di.driverClass = driver.getClass();  
  4. di.driverClassName = di.driverClass.getName();  

然后添加到集合writeDrivers中,这个集合是Vector类型,定义为DriverManager的属性

writeDrivers定义:

[java] view plaincopy
  1. private static java.util.Vector writeDrivers = new java.util.Vector();  

驱动添加到集合

[java] view plaincopy
  1. writeDrivers.addElement(di);  

最后就是调用writeDrivers对象的clone方法

[java] view plaincopy
  1. readDrivers = (java.util.Vector) writeDrivers.clone();  


readDrivers也是一个类型为Vector的集合,定义为DriverManager的属性

[java] view plaincopy
  1. private static java.util.Vector readDrivers = new java.util.Vector();  

为什么要先添加到writeDrivers然后再 clone到readDrivers中呢?
writeDrivers和 readDrivers两个都是驱动集合,无论是注册驱动抑或是取消注册,都是先对writeDrivers驱动集合中的数据进行添加或删除,然后再把writeDrivers中的驱动都clone到readDrivers中,每次取出Driver并不是在writeDrivers中,而是在readDrivers中取得。那么这两个驱动集合便可以这样理解,writeDrivers驱动集合负责注册驱动和注销驱动,readDrivers驱动集合负责提供可用的驱动对象,readDrivers中的驱动对象应该都是可用的。把二者分开,使用者就不需加任何判断,很方便。
这里又涉及到一个知识就是clone, 有兴趣的朋友可以查看相关JAVA文档,Thinking in java 中也有详细描述。
这就是初始化的全过程,写了这么多,实际上只做一件事情,就是完成所有驱动的加载。装载之后就是连接了,在连载三当中我会详细描述。

0 0