第二篇中讲解了如何装载驱动,以及它的初始化过程。
本篇分析一下连接数据库时使用的获取数据库连接的代码:




    1. DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123”);
    DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123”);



    DriverManager.getConnection方法有三种重载方式,这里我们使用带有三个参数的方法,第一个表示数据库的URL, 第二、三个分别
    是用户名和密码。获取数据库连接如此简单,只要把三个参数准确无误的写上去,连接肯定就能获取。但是连接方法中到底给我们做
    了哪些工作呢? 下面找到DriverManager类的静态方法getConnection源码一探究竟。

    1. public static
    2. throws
    3. new java.util.Properties(); ///1
    4. // Gets the classloader of the code that called this method, may 
    5. // be null.
    6. ///2
    7. if (user != null) {  
    8. ”user”, user);  
    9.     }  
    10. if (password != null) {  
    11. ”password”, password);  
    12.     }  
    13. return (getConnection(url, info, callerCL)); ///3
    14.     }

    public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); ///1 // Gets the classloader of the code that called this method, may // be null. ClassLoader callerCL = DriverManager.getCallerClassLoader(); ///2 if (user != null) { info.put(“user”, user); } if (password != null) { info.put(“password”, password); } return (getConnection(url, info, callerCL)); ///3 }


    1处定义了一个Properties对象,java.util.Properties它是用来在文件中存储键值对的,其中键和值是用等号分隔的。可以将值以
    key-value的形式存入Properties.
    2处调用方法getCallerClassLoader得到类装载器对象, 这个地方有点意思,既然是调用方法,怎么变成了下面这种形式呢?根本就
    不见方法体。
    源码:



      1. private static native
       private static native ClassLoader getCallerClassLoader();


      getCallerClassLoader()是一个静态原生方法,返回类型为ClassLoader, 被声明为native, 说明这个方法是一个原生方法,也就是说
      这个方法是用C/C++语言实现的,并且被编译成了DLL,由JAVA调用。这些函数的实体在DLL中,JDK源码并不包含,在JAVA源文件中是
      找不到源代码的。不同的平台其实现也有所差异。这也是JAVA的底层机制,实际上JAVA就是在不同的平台上调用不同的native方法实
      现对操作系统的访问。native关键字一般是和C/C++联合开发的时候使用。如果标明为native 要求运行时通知操作系统,这个函数必
      须给我实现,JAVA需要调用。如果未实现,那么调用时会抛出一个异常java.lang.UnsatisfiedLinkError
      接下来判断用户名和密码,并将其存放到Properties对象中:



      1. if (user != null) {  
      2. ”user”, user);  
      3.     }  
      4. if (password != null) {  
      5. ”password”, password);  
      6.     }

      if (user != null) { info.put(“user”, user); } if (password != null) { info.put(“password”, password); }


      调用获取连接的另一个重载方法
      getCollection(String url, java.util.Properties info, ClassLoader callerCL)



        1. return
        return (getConnection(url, info, callerCL));



        getConnection(url, info, callerCL) 源码:





        1. private static
        2. String url, java.util.Properties info, ClassLoader callerCL) throws
        3. java.util.Vector drivers = null;  
        4. synchronized(DriverManager.class) {    
        5. // synchronize loading of the correct classloader.
        6. if(callerCL == null) {  
        7.       callerCL = Thread.currentThread().getContextClassLoader();  
        8.    }      
        9. }   
        10.    
        11. if(url == null) {  
        12. throw new SQLException(“The url cannot be null”, “08001”);  
        13. }  
        14.      
        15. println(”DriverManager.getConnection(/”“ + url + ”/“)”);  
        16.      
        17. if
        18.     initialize();  
        19. }  
        20. synchronized (DriverManager.class){   
        21. // use the readcopy of drivers
        22.     drivers = readDrivers;    
        23.        }  
        24. // Walk through the loaded drivers attempting to make a connection.
        25. // Remember the first exception that gets raised so we can reraise it.
        26. SQLException reason = null;  
        27. for (int i = 0; i < drivers.size(); i++) {  
        28.     DriverInfo di = (DriverInfo)drivers.elementAt(i);  
        29.        
        30. // If the caller does not have permission to load the driver then 
        31. // skip it.
        32. if
        33. ”    skipping: ”
        34. continue;  
        35.     }  
        36. try
        37. ”    trying ”
        38.     Connection result = di.driver.connect(url, info);  
        39. if (result != null) {  
        40. // Success!
        41. ”getConnection returning ”
        42. return
        43.     }  
        44. catch
        45. if (reason == null) {  
        46.         reason = ex;  
        47.     }  
        48.     }  
        49. }  
        50.      
        51. // if we got here nobody could connect.
        52. if (reason != null)    {  
        53. ”getConnection failed: ”
        54. throw
        55. }  
        56.      
        57. println(”getConnection: no suitable driver found for ”+ url);  
        58. throw new SQLException(“No suitable driver found for ”+ url, “08001”);  
        59.    }

        private static Connection getConnection( String url, java.util.Properties info, ClassLoader callerCL) throws SQLException { java.util.Vector drivers = null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if(callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException(“The url cannot be null”, “08001”); } println(“DriverManager.getConnection(/”” + url + “/”)”); if (!initialized) { initialize(); } synchronized (DriverManager.class){ // use the readcopy of drivers drivers = readDrivers; } // Walk through the loaded drivers attempting to make a connection. // Remember the first exception that gets raised so we can reraise it. SQLException reason = null; for (int i = 0; i < drivers.size(); i++) { DriverInfo di = (DriverInfo)drivers.elementAt(i); // If the caller does not have permission to load the driver then // skip it. if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) { println(” skipping: ” + di); continue; } try { println(” trying ” + di); Connection result = di.driver.connect(url, info); if (result != null) { // Success! println(“getConnection returning ” + di); return (result); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } // if we got here nobody could connect. if (reason != null) { println(“getConnection failed: ” + reason); throw reason; } println(“getConnection: no suitable driver found for “+ url); throw new SQLException(“No suitable driver found for “+ url, “08001”); }


        此方法代码较多,我们一步步来分析。
        1、    传参
        从前面的那个getConnection方法中形成的三个参数传递到这个getConnection方法中, 参数包括连接数据库的URL,包装了用户名和
        密码的对象info, 通过调用原生函数返回的类装载器callerCL
        2、    同步DriverManager.class
        同步DriverManager的Class对象,synchronized同步的对象为DriverManager.class,是为同步正确的类加载器来加载类,在同步块
        中判断传入的类装载器对象是否存在,如果为null, 通过当前线程来获取上下文类装载器,保证JDBC驱动程序类以外的rt.jar中的类
        可以在这里被加载。有关于Thread和synchronized读者可以参考java多线程编程

        1. synchronized(DriverManager.class) {    
        2. if(callerCL == null) {  
        3.           callerCL = Thread.currentThread().getContextClassLoader();  
        4.        }  
        5.     }

        synchronized(DriverManager.class) { if(callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } }


        3、    判断URL,如果为null则抛出SQLException异常
        判断initialized的值,如果未初始化,继续调用初始化方法,此处在第二篇中已详细解释初始化过程,初始化之后writeDrivers 和readDrivers 将会有系统所有驱动数据,接下来使用synchronized同步DriverManager.class对象, 将方法中定义的集合drivers 引用 readDrivers对象,readDrivers是从writeDrivers拷贝过来



        1. if(url == null) {  
        2. throw new SQLException(“The url cannot be null”, “08001”);  
        3. }  
        4. println(”DriverManager.getConnection(/”“ + url + ”/“)”);  
        5. if
        6. initialize();  
        7. }  
        8. synchronized (DriverManager.class) {   
        9. // use the readcopy of drivers
        10. drivers = readDrivers;    
        11.  }

        if(url == null) { throw new SQLException(“The url cannot be null”, “08001”); } println(“DriverManager.getConnection(/”” + url + “/”)”); if (!initialized) { initialize(); } synchronized (DriverManager.class) { // use the readcopy of drivers drivers = readDrivers; }


        4、    遍历驱动
        通过for循环,遍历drivers集合,其中每个元素的类型为DriverInfo, 这个在第二篇中也有详细描述
        首先取得集合中的每一个对象元素,调用getCallerClass()方法


        1. for (int i = 0; i < drivers.size(); i++) {  
        2.         DriverInfo di = (DriverInfo)drivers.elementAt(i);  
        3. if
        4. ”    skipping: ”
        5. continue;  
        6.         }  
        7. ……  
        8. }

        for (int i = 0; i < drivers.size(); i++) { DriverInfo di = (DriverInfo)drivers.elementAt(i); if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) { println(” skipping: ” + di); continue; } …… }


        getCallerClass方法源码:


        1. private static
        2.                     String driverClassName) {  
        3. Class callerC = null;  
        4. try
        5. callerC = Class.forName(driverClassName, true, callerClassLoader);  
        6. }  
        7. catch
        8. null;             
        9.     }  
        10. return
        11.     }

        private static Class getCallerClass(ClassLoader callerClassLoader, String driverClassName) { Class callerC = null; try { callerC = Class.forName(driverClassName, true, callerClassLoader); } catch (Exception ex) { callerC = null; } return callerC; }


            这个方法返回一个Class对象,通过指定的类装载器来装载驱动类。这个方法内做得非常小心,如果出现异常都会把需要返
        回的Class对象置为null.
            在if语句中调用getCallerClass方法得到的Class 对象和每一个驱动的Class对象比较,不相等的话就继续执行下一次循环
        ,否则都调用Driver的connect方法,传入url, 和 info,通过这个方法返回Connection连接对象


        1. Connection result = di.driver.connect(url, info);  
        Connection result = di.driver.connect(url, info);


         

        Driver接口的实现类中的connect方法具体所做的工作将在下一篇中详述



        第二篇中讲解了如何装载驱动,以及它的初始化过程。
        本篇分析一下连接数据库时使用的获取数据库连接的代码:



          1. DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123”);
          DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123”);


          DriverManager.getConnection方法有三种重载方式,这里我们使用带有三个参数的方法,第一个表示数据库的URL, 第二、三个分别
          是用户名和密码。获取数据库连接如此简单,只要把三个参数准确无误的写上去,连接肯定就能获取。但是连接方法中到底给我们做
          了哪些工作呢? 下面找到DriverManager类的静态方法getConnection源码一探究竟。



          1. public static
          2. throws
          3. new java.util.Properties(); ///1
          4. // Gets the classloader of the code that called this method, may 
          5. // be null.
          6. ///2
          7. if (user != null) {  
          8. ”user”, user);  
          9.     }  
          10. if (password != null) {  
          11. ”password”, password);  
          12.     }  
          13. return (getConnection(url, info, callerCL)); ///3
          14.     }

          public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); ///1 // Gets the classloader of the code that called this method, may // be null. ClassLoader callerCL = DriverManager.getCallerClassLoader(); ///2 if (user != null) { info.put(“user”, user); } if (password != null) { info.put(“password”, password); } return (getConnection(url, info, callerCL)); ///3 }


          1处定义了一个Properties对象,java.util.Properties它是用来在文件中存储键值对的,其中键和值是用等号分隔的。可以将值以
          key-value的形式存入Properties.
          2处调用方法getCallerClassLoader得到类装载器对象, 这个地方有点意思,既然是调用方法,怎么变成了下面这种形式呢?根本就
          不见方法体。
          源码:


            1. private static native
             private static native ClassLoader getCallerClassLoader();



            getCallerClassLoader()是一个静态原生方法,返回类型为ClassLoader, 被声明为native, 说明这个方法是一个原生方法,也就是说
            这个方法是用C/C++语言实现的,并且被编译成了DLL,由JAVA调用。这些函数的实体在DLL中,JDK源码并不包含,在JAVA源文件中是
            找不到源代码的。不同的平台其实现也有所差异。这也是JAVA的底层机制,实际上JAVA就是在不同的平台上调用不同的native方法实
            现对操作系统的访问。native关键字一般是和C/C++联合开发的时候使用。如果标明为native 要求运行时通知操作系统,这个函数必
            须给我实现,JAVA需要调用。如果未实现,那么调用时会抛出一个异常java.lang.UnsatisfiedLinkError
            接下来判断用户名和密码,并将其存放到Properties对象中:


            1. if (user != null) {  
            2. ”user”, user);  
            3.     }  
            4. if (password != null) {  
            5. ”password”, password);  
            6.     }

            if (user != null) { info.put(“user”, user); } if (password != null) { info.put(“password”, password); }


            调用获取连接的另一个重载方法
            getCollection(String url, java.util.Properties info, ClassLoader callerCL)


              1. return
              return (getConnection(url, info, callerCL));



              getConnection(url, info, callerCL) 源码:



              1. private static
              2. String url, java.util.Properties info, ClassLoader callerCL) throws
              3. java.util.Vector drivers = null;  
              4. synchronized(DriverManager.class) {    
              5. // synchronize loading of the correct classloader.
              6. if(callerCL == null) {  
              7.       callerCL = Thread.currentThread().getContextClassLoader();  
              8.    }      
              9. }   
              10.    
              11. if(url == null) {  
              12. throw new SQLException(“The url cannot be null”, “08001”);  
              13. }  
              14.      
              15. println(”DriverManager.getConnection(/”“ + url + ”/“)”);  
              16.      
              17. if
              18.     initialize();  
              19. }  
              20. synchronized (DriverManager.class){   
              21. // use the readcopy of drivers
              22.     drivers = readDrivers;    
              23.        }  
              24. // Walk through the loaded drivers attempting to make a connection.
              25. // Remember the first exception that gets raised so we can reraise it.
              26. SQLException reason = null;  
              27. for (int i = 0; i < drivers.size(); i++) {  
              28.     DriverInfo di = (DriverInfo)drivers.elementAt(i);  
              29.        
              30. // If the caller does not have permission to load the driver then 
              31. // skip it.
              32. if
              33. ”    skipping: ”
              34. continue;  
              35.     }  
              36. try
              37. ”    trying ”
              38.     Connection result = di.driver.connect(url, info);  
              39. if (result != null) {  
              40. // Success!
              41. ”getConnection returning ”
              42. return
              43.     }  
              44. catch
              45. if (reason == null) {  
              46.         reason = ex;  
              47.     }  
              48.     }  
              49. }  
              50.      
              51. // if we got here nobody could connect.
              52. if (reason != null)    {  
              53. ”getConnection failed: ”
              54. throw
              55. }  
              56.      
              57. println(”getConnection: no suitable driver found for ”+ url);  
              58. throw new SQLException(“No suitable driver found for ”+ url, “08001”);  
              59.    }

              private static Connection getConnection( String url, java.util.Properties info, ClassLoader callerCL) throws SQLException { java.util.Vector drivers = null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if(callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException(“The url cannot be null”, “08001”); } println(“DriverManager.getConnection(/”” + url + “/”)”); if (!initialized) { initialize(); } synchronized (DriverManager.class){ // use the readcopy of drivers drivers = readDrivers; } // Walk through the loaded drivers attempting to make a connection. // Remember the first exception that gets raised so we can reraise it. SQLException reason = null; for (int i = 0; i < drivers.size(); i++) { DriverInfo di = (DriverInfo)drivers.elementAt(i); // If the caller does not have permission to load the driver then // skip it. if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) { println(” skipping: ” + di); continue; } try { println(” trying ” + di); Connection result = di.driver.connect(url, info); if (result != null) { // Success! println(“getConnection returning ” + di); return (result); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } // if we got here nobody could connect. if (reason != null) { println(“getConnection failed: ” + reason); throw reason; } println(“getConnection: no suitable driver found for “+ url); throw new SQLException(“No suitable driver found for “+ url, “08001”); }


              此方法代码较多,我们一步步来分析。
              1、    传参
              从前面的那个getConnection方法中形成的三个参数传递到这个getConnection方法中, 参数包括连接数据库的URL,包装了用户名和
              密码的对象info, 通过调用原生函数返回的类装载器callerCL
              2、    同步DriverManager.class
              同步DriverManager的Class对象,synchronized同步的对象为DriverManager.class,是为同步正确的类加载器来加载类,在同步块
              中判断传入的类装载器对象是否存在,如果为null, 通过当前线程来获取上下文类装载器,保证JDBC驱动程序类以外的rt.jar中的类
              可以在这里被加载。有关于Thread和synchronized读者可以参考java多线程编程



              1. synchronized(DriverManager.class) {    
              2. if(callerCL == null) {  
              3.           callerCL = Thread.currentThread().getContextClassLoader();  
              4.        }  
              5.     }

              synchronized(DriverManager.class) { if(callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } }


              3、    判断URL,如果为null则抛出SQLException异常
              判断initialized的值,如果未初始化,继续调用初始化方法,此处在第二篇中已详细解释初始化过程,初始化之后writeDrivers 和readDrivers 将会有系统所有驱动数据,接下来使用synchronized同步DriverManager.class对象, 将方法中定义的集合drivers 引用 readDrivers对象,readDrivers是从writeDrivers拷贝过来



              1. if(url == null) {  
              2. throw new SQLException(“The url cannot be null”, “08001”);  
              3. }  
              4. println(”DriverManager.getConnection(/”“ + url + ”/“)”);  
              5. if
              6. initialize();  
              7. }  
              8. synchronized (DriverManager.class) {   
              9. // use the readcopy of drivers
              10. drivers = readDrivers;    
              11.  }

              if(url == null) { throw new SQLException(“The url cannot be null”, “08001”); } println(“DriverManager.getConnection(/”” + url + “/”)”); if (!initialized) { initialize(); } synchronized (DriverManager.class) { // use the readcopy of drivers drivers = readDrivers; }


              4、    遍历驱动
              通过for循环,遍历drivers集合,其中每个元素的类型为DriverInfo, 这个在第二篇中也有详细描述
              首先取得集合中的每一个对象元素,调用getCallerClass()方法



              1. for (int i = 0; i < drivers.size(); i++) {  
              2.         DriverInfo di = (DriverInfo)drivers.elementAt(i);  
              3. if
              4. ”    skipping: ”
              5. continue;  
              6.         }  
              7. ……  
              8. }

              for (int i = 0; i < drivers.size(); i++) { DriverInfo di = (DriverInfo)drivers.elementAt(i); if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) { println(” skipping: ” + di); continue; } …… }


              getCallerClass方法源码:




              1. private static
              2.                     String driverClassName) {  
              3. Class callerC = null;  
              4. try
              5. callerC = Class.forName(driverClassName, true, callerClassLoader);  
              6. }  
              7. catch
              8. null;             
              9.     }  
              10. return
              11.     }

              private static Class getCallerClass(ClassLoader callerClassLoader, String driverClassName) { Class callerC = null; try { callerC = Class.forName(driverClassName, true, callerClassLoader); } catch (Exception ex) { callerC = null; } return callerC; }


                  这个方法返回一个Class对象,通过指定的类装载器来装载驱动类。这个方法内做得非常小心,如果出现异常都会把需要返
              回的Class对象置为null.
                  在if语句中调用getCallerClass方法得到的Class 对象和每一个驱动的Class对象比较,不相等的话就继续执行下一次循环
              ,否则都调用Driver的connect方法,传入url, 和 info,通过这个方法返回Connection连接对象


              1. Connection result = di.driver.connect(url, info);  
              Connection result = di.driver.connect(url, info);


               

              Driver接口的实现类中的connect方法具体所做的工作将在下一篇中详述