• JDBC底层原理


    Class.forName(“com.mysql.jdbc.Driver”)是 强制JVM将com.mysql.jdbc.Driver这个类加载入内存,并将其注册到DriverManager类,然后根据DriverManager.getConnection(url,user,pwd)中的url找到相应的驱动类,最后调用该该驱动类的connect(url, info)来获得connection对象。

    JDBC的驱动管理机制的 具体底层代码分析如下:

        

       

    1.     分析JDBC的驱动程序管理部分的实现代码:
        
            在 JDBC的层次上,sun主要定义了1个接口Driver和两个类:DirverManager和DriverInfo。每个JDBC驱动程序必须实现 Driver接口(在MySql的Connector/J驱动中,这个叫做com.mysql.jdbc.Driver)。而DriverManager 则负责管理所有的Driver对象,包含注册Driver;选择合适的Driver来建立到某个数据库的连接;以及进行一些Driver的信息管理等。 DriverInfo非常简单,用于保存Driver的信息,只有3个成员变量,Driver,DriverClass和 DriverClassName,意义非常明显。

            先看一下在DriverManager.java中的关键代码:
            private static java.util.Vector drivers = new java.util.Vector();
            所有的Driver对象保存在一个Vector数组中。

            注册Driver的函数叫registerDriver,将需要注册的Driver对象传入即可:
            

    复制代码
    public static synchronized void registerDriver(java.sql.Driver driver)
             throws SQLException {
                 if (!initialized) {                                    //如果没有初始化,则先初始化
                     initialize();
                 }      
                 DriverInfo di = new DriverInfo();         // 实际保存的不是Driver,而是一个DriverInfo对象,但是DriverInfo的其它成员完全可以由Driver推导出来,所以个人觉得 DriverInfo对象可有可无,直接使用Driver应该就可以了。
                 di.driver = driver;
                 di.driverClass = driver.getClass();
                 di.driverClassName = di.driverClass.getName();
                 drivers.addElement(di);                        //将DriverInfo对象添加到数组中
                 println("registerDriver: " + di);
            }
    复制代码

    在一个类加载入内存的时候,类中的静态初始化过程会执行,这样就完成了驱动程序的注册过程。然后重点看一下建立数据库连接的代码,在getConnection函数中,省略了一些非关键代码:
                

    复制代码
    private static synchronized Connection getConnection(
     String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {
    
             SQLException reason = null;
             //轮询所有的DriverInfo对象。
             for (int i = 0; i < drivers.size(); i++) {
                 DriverInfo di = (DriverInfo)drivers.elementAt(i);
                 try {
                      //使用DriverInfo中的Driver对象去做实际的连接数据库的工作
                      Connection result = di.driver.connect(url, info);
                      if (result != null) {
                          // Success!
                          println("getConnection returning " +&bsp;di);
                          return (result);    //一旦成功连接,直接返回Connection对象,然后推出
                      }
                     } catch (SQLException ex) {
                            ...
                      }
                 }        
                 //这就是经常看到的出错信息--找不到合适的驱动程序。
                 println("getConnection: no suitable driver");    
                 throw new SQLException("No suitable driver", "08001");
            }
    复制代码


            由 上面的getConnection函数可以看到,真正实现数据库连接的是Driver对象的connect函数。而且可以看到,由于 DriverManager.getConnection使用的是一种轮询的方式,注册的驱动程序越多,连接速度会越慢。JDBC连接数据库的速度很慢, 是不是和这种实现方式有关联呢?怀着这个问题,本人下载了MySql的Connector/J驱动包,开始分析其connect函数的实现。

    2.    分析MySql的注册和建立连接部分的代码:
            打开MySql的源码包,首先分析其Driver类的实现。发现Driver类的实现非常简单,
            

    复制代码
    public class Driver extends NonRegisteringDriver implements java.sql.Driver {
            static {
                    try {
                        java.sql.DriverManager.registerDriver(new Driver());
                        } catch (SQLException E) {
                            throw new RuntimeException("Can't register driver!");
                    }
                } 
                   public Driver() throws SQLException {
                    // Required for Class.forName().newInstance()
            }            
    复制代码


            可 以看到,有一段static代码,调用了DriverManager的registerDriver方法。这其实就解释了 Class.forName(“com.mysql.jdbc.Driver”)能够完成MySql驱动注册的问题。因为forName会导致这段 static代码被调用,从而间接调用了registerDriver,完成注册过程。
            
            com.mysql.jdbc.Driver 从com.mysql.jdbc.NonRegisteringDriver继承而来,实际上是NonReisteringDriver完成了 java.sql.Driver接口的实现工作。转移目标,分析NonRegisteringDriver的connect函数。

            NonRegisteringDriver.connect的实现也比较简单,正合我意:

            public java.sql.Connection connect(String url, Properties info)
            throws SQLException {

            //1.    分析传入的连接字符串.
           

    复制代码
     if ((props = parseURL(url, info)) == null) {        
                return null;
            }
            try {
                //2.     建立一个Connection对象完成实际的数据库连接工作
                Connection newConn = new com.mysql.jdbc.Connection(host(props),
                        port(props), props, database(props), url, this);
                return newConn;
    
      }
    复制代码

            非 常简单,先parseURL,然后使用Connection去建立连接。parseURL只是简单的字符串分析,主要是分析传入的连接字符串是否满足 “jdbc:mysql://host:port/database“的格式,如果不满足,直接返回null,然后由DriverManager去试验下 一个Driver。如果满足,则建立一个Connection对象建立实际的数据库连接,这不是本人关注的问题,源码分析就此打住。

          由此可见1中的问题答案是:DriverManager的轮询查询注册的Driver对象的工作方式所带来的性能代价并不是很大,主工作量只是parseURL函数。

  • 相关阅读:
    04-set方法的内存管理
    03-OC内存管理原则
    02-引用计数器的使用(含野指针、空指针)
    01-OC内存管理的基本原理
    16-NSDate
    15-NSValue
    14-NSNumber
    13-NSMutableDictionary
    12-NSDictionary(字典)
    11-NSArray和NSSet的区别
  • 原文地址:https://www.cnblogs.com/kxdblog/p/4817774.html
Copyright © 2020-2023  润新知