注: 转自http://blog.youkuaiyun.com/brilliancezhou/article/details/5425655
JAVA连接数据库是其众多功能中的一部分,主要有两种方式连接DataBase: 一种是采用JDBC-ODBC桥,另一种则是称之为纯驱动连接DataBase,第一种方式在大型项目中基本上不再使用,本系列文章主要分析纯驱动源码。
对于初学JAVA者,甚至那些使用JAVA做过几年开发的程序员来讲,对于JDBC的工作原理都不一定能够明白。知其然,不知其所以然。遇到问题就不知所措了。通过针对于MYSQL JDBC源码的分析,对于JAVA是如何连接数据库,其中到底做了些什么工作,一步步解剖开来,更好的理解JDBC。
使用JAVA连接数据库,首先要做的就是在程序中导入sql包,然后装载驱动类、获取连接、获取语句对象、发送SQL命令然后得到结果
请看以下代码片段:
- /* 连接mysql 时装载的驱动类以及连接字符串 */
- Class.forName(“com.mysql.jdbc.Driver”);//1
- DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123”);//2
- /* 连接SQLServer2005 时装载的驱动类以及连接字符串 */
- Class.forName(“com.microsoft.sqlserver.jdbc.SQLServerDriver”);
- 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类
- package com.mysql.jdbc;
- import java.sql.SQLException;
- public class Driver extends NonRegisteringDriver implements java.sql.Driver {
- static {
- try {
- java.sql.DriverManager.registerDriver(new Driver()); //1
- } catch (SQLException E) {
- throw new RuntimeException("Can't register driver!");
- }
- }
- // ~ Constructors
- // -----------------------------------------------------------
- /**
- * Construct a new driver and register it with DriverManager
- *
- * @throws SQLException
- * if a database error occurs.
- */
- public Driver() throws SQLException {
- // Required for Class.forName().newInstance()
- }
- }
Driver类继承NonRegisteringDriver 同时实现接口java.sql.Driver
此类会有一个静态块
- static {
- try {
- java.sql.DriverManager.registerDriver(new Driver()); //1
- } catch (SQLException E) {
- throw new RuntimeException("Can't register driver!");
- }
- }
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对象,所以此类会有一个无参数的构造函数:
- public Driver() throws SQLException {
- }
下面再看看DriverManager.registerDriver()这个方法,源码如下:
- public static synchronized void registerDriver(java.sql.Driver driver)
- throws SQLException {
- if (!initialized) {
- initialize();
- }
- DriverInfo di = new DriverInfo();
- di.driver = driver;
- di.driverClass = driver.getClass();
- di.driverClassName = di.driverClass.getName();
- // Not Required -- drivers.addElement(di);
- writeDrivers.addElement(di);
- println("registerDriver: " + di);
- /* update the read copy of drivers vector */
- readDrivers = (java.util.Vector) writeDrivers.clone();
- }
此方法是一个静态同步的方法,形式参数是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,那么加载的也就是它的驱动。
此方法的源码如下:
- public static synchronized void registerDriver(java.sql.Driver driver)
- throws SQLException {
- if (!initialized) { //1
- initialize();
- }
- DriverInfo di = new DriverInfo();
- di.driver = driver;
- di.driverClass = driver.getClass();
- di.driverClassName = di.driverClass.getName();
- // Not Required -- drivers.addElement(di);
- writeDrivers.addElement(di);
- println("registerDriver: " + di);
- /* update the read copy of drivers vector */
- readDrivers = (java.util.Vector) writeDrivers.clone();
- }
一、初始化操作
1、看看1处的代码,判断是否初始化,这个判断的变量是一个静态全局boolean值,初始为false
private static boolean initialized = false;
如果此变量的值为false那么它将会进入初始化方法,源码如下:
- static void initialize() {
- if (initialized) {
- return;
- }
- initialized = true;
- loadInitialDrivers();
- println("JDBC DriverManager initialized");
- }
2、Initialize方法中判断initialized值是否为真(其实就是通过此boolean变量判断是否已经初始化完成),之后设置initialized值为true,接着又会调用另一个方法loadInitialDrivers() 同样是静态方法,用于调用系统类装载器,装载所有系统提供的驱动:
loadInitialDrivers()源码:
- private static void loadInitialDrivers() {
- String drivers;
- try {
- drivers = (String) java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction("jdbc.drivers"));
- } catch (Exception ex) {
- drivers = null;
- }
- // If the driver is packaged as a Service Provider,
- // load it.
- // Get all the drivers through the classloader
- // exposed as a java.sql.Driver.class service.
- DriverService ds = new DriverService();
- // Have all the privileges to get all the
- // implementation of java.sql.Driver
- java.security.AccessController.doPrivileged(ds);
- println("DriverManager.initialize: jdbc.drivers = " + drivers);
- if (drivers == null) {
- return;
- }
- while (drivers.length() != 0) {
- int x = drivers.indexOf(':');
- String driver;
- if (x < 0) {
- driver = drivers;
- drivers = "";
- } else {
- driver = drivers.substring(0, x);
- drivers = drivers.substring(x+1);
- }
- if (driver.length() == 0) {
- continue;
- }
- try {
- println("DriverManager.Initialize: loading " + driver);
- Class.forName(driver, true,
- ClassLoader.getSystemClassLoader());
- } catch (Exception ex) {
- println("DriverManager.Initialize: load failed: " + ex);
- }
- }
- }
主要代码分析:
下面这段创建了一个内部类对象,创建此对象时,它会从系统服务中加载驱动
- DriverService ds = new DriverService();
DriverService内部类的具体代码:
- class DriverService implements java.security.PrivilegedAction {
- Iterator ps = null;
- public DriverService() {};
- public Object run() {
- ps = Service.providers(java.sql.Driver.class); //从系统服务中加载驱动
- try {
- while (ps.hasNext()) { //遍历驱动
- ps.next();
- } // end while
- } catch(Throwable t) {
- // Do nothing
- }
- return null;
- } //end run
- } //end DriverService
此句代码就是找到所有的拥有权限的java.sql.Driver的实现
- java.security.AccessController.doPrivileged(ds);
下面这段,意思是得到系统属性jdbc.drivers对应驱动的驱动名称,使用了JAVA的安全许可
- drivers = (String) java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction("jdbc.drivers"));
再看看后面的判断和循环
首先判断驱动服务对象是否为null,如果为null则返回,否则进入while循环,这个循环会依次遍历多个数据库驱动,因为jdbc:drivers会有多个数据库驱动,驱动名是以:分割,接下来就是通过Class.forName依次装载驱动类,在其中使用了ClassLoader.getSystemClassLoader()系统类装载器。
- if (drivers == null) {
- return;
- }
- while (drivers.length() != 0) {
- …
- Class.forName(driver, true, ClassLoader.getSystemClassLoader());
- …
- }
上面分析的就是在registerDriver方法中所要做的第一件事情:初始化。可以看到initialize()做的工作就是装载驱动,同时还需要使用到系统的一些功能。如: java.security.AccessController.doPrivileged,此方法允许在一个类实例中的代码通知这个AccessController,它的代码主体享受特权(Privileged),它不管这个请求是由什么代码所引发的,只是单独负责对它可得到的资源的访问请求。比如说,一个调用者在调用doPrivileged方法时,可被标识为特权。AccessController做访问控制决策时,如果checkPermission方法遇到一个通过doPrivileged方法调用而被视为特权调用者,那么checkPermission方法不会作许可检查,表示那个访问请求是被允许的,如果调用者没有许可,则会抛出一个异常。
如:ClassLoader.getSystemClassLoader(),java中所有类都是通过ClassLoader装载的,ClassLoader可以为java程序提供很好的动态特性,有必要去深入理解哦。
接下来再看初始化之后的代码:
- DriverInfo di = new DriverInfo();
- di.driver = driver;
- di.driverClass = driver.getClass();
- di.driverClassName = di.driverClass.getName();
- // Not Required -- drivers.addElement(di);
- writeDrivers.addElement(di);
- println("registerDriver: " + di);
- /* update the read copy of drivers vector */
- readDrivers = (java.util.Vector) writeDrivers.clone();
创建DriverInfo对象
DriverInfo di = new DriverInfo();
DriverInfo驱动信息类,是一个内部类,
源码如下:
- class DriverInfo {
- Driver driver;
- Class driverClass;
- String driverClassName;
- public String toString() {
- return ("driver[className=" + driverClassName + "," + driver + "]");
- }
- }
此类就是添加了三个属性,分别表示驱动对象,驱动的Class对象,以及驱动的类名;同时重写了toString方法。此内部类的作用就是以可以创建DriverInfo对象,以对象的形式保存驱动信息。
接下来就是设置对象的三个属性:
- DriverInfo di = new DriverInfo();
- di.driver = driver;
- di.driverClass = driver.getClass();
- di.driverClassName = di.driverClass.getName();
然后添加到集合writeDrivers中,这个集合是Vector类型,定义为DriverManager的属性
writeDrivers定义:
- private static java.util.Vector writeDrivers = new java.util.Vector();
驱动添加到集合
- writeDrivers.addElement(di);
最后就是调用writeDrivers对象的clone方法
- readDrivers = (java.util.Vector) writeDrivers.clone();
readDrivers也是一个类型为Vector的集合,定义为DriverManager的属性
- 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 中也有详细描述。
这就是初始化的全过程,写了这么多,实际上只做一件事情,就是完成所有驱动的加载。装载之后就是连接了,在连载三当中我会详细描述。