20170415_DriverManager获取Connecti

2017-07-15  本文已影响0人  mingmingz

问题的引出

为什么我们通过DriverManager.getConnection(url, username, password)获取到的是Connection是一个接口,却能调用方法?不是说接口只是一个抽象的东西?不能做具体的事情么?
那我们通过DriverManager获取到的Connection接口为什么可以做获取Statement对象之类的事情?

首先我们看我们获取到的对象是怎么样获取的:

    Connection  conn = DriverManager.getConnection(url, username, password);

这种获取方法是不是很眼熟?我们看一下从小学到大的一个创建对象的写法:

    Person person=new Student();

这样来看是不是很熟悉?是不是我们的父类引用指向子类对象?

所以我们实际上通过DriverManager.getConnection(url, username, password)获取到的是实现了Connection接口的实体对象

我们接着看DriverManager是怎么获取到Connection实体对象的,先通过DriverManager.getConnection(url, username, password)跳到源码位置

DvierManamger的方法

        public static Connection getConnection(String url,
            String user, String password) throws SQLException {
            java.util.Properties info = new java.util.Properties();//创建了一个Properties对象来存放信息
    
            if (user != null) {
                info.put("user", user);//如果user不为空,将user信息存入info对象
            }
            if (password != null) {
                info.put("password", password);//如果password不为空,将password信息存入info对象
            }
    
            //再通过另一个重载的getConnection方法获取连接
            return (getConnection(url, info, Reflection.getCallerClass()));
        }

我们在第一次跳转进来的方法中是没法直接获取实现了Connection接口的实体类的,这里还要通过DriverManger的重载方法继续做做事情,接着我们进到getConnection(url, info, Reflection.getCallerClass())方法

        private static Connection getConnection(
                String url, java.util.Properties info, Class<?> caller) throws SQLException {
                /*
                 * When callerCl is null, we should check the application's
                 * (which is invoking this class indirectly)
                 * classloader, so that the JDBC driver class outside rt.jar
                 * can be loaded from here.
                 */
                ClassLoader callerCL = caller != null ? caller.getClassLoader() : 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 + "\")");
        
                // Walk through the loaded registeredDrivers attempting to make a connection.
                // Remember the first exception that gets raised so we can reraise it.
                SQLException reason = null;
        
                for(DriverInfo aDriver : registeredDrivers) {
                    // If the caller does not have permission to load the driver then
                    // skip it.
                    if(isDriverAllowed(aDriver.driver, callerCL)) {
                        try {
                            println("    trying " + aDriver.driver.getClass().getName());
                            Connection con = aDriver.driver.connect(url, info);
                            if (con != null) {
                                // Success!
                                println("getConnection returning " + aDriver.driver.getClass().getName());
                                return (con);
                            }
                        } catch (SQLException ex) {
                            if (reason == null) {
                                reason = ex;
                            }
                        }
        
                    } else {
                        println("    skipping: " + aDriver.getClass().getName());
                    }
        
                }
        
                // 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");
            }

这一整个方法我们不用全部关心,找到切入点,我们现在知道自己在找Connection,和Connection息息相关的是Driver,那我们现在就针对这两个对象来查找,可以看到这个方法里的这段关键代码

     for(DriverInfo aDriver : registeredDrivers) {
                // If the caller does not have permission to load the driver then
                // skip it.
                if(isDriverAllowed(aDriver.driver, callerCL)) {
                    try {
                        println("    trying " + aDriver.driver.getClass().getName());
                        Connection con = aDriver.driver.connect(url, info);
                        if (con != null) {
                            // Success!
                            println("getConnection returning " + aDriver.driver.getClass().getName());
                            return (con);
                        }
                    } catch (SQLException ex) {
                        if (reason == null) {
                            reason = ex;
                        }
                    }
    
                } else {
                    println("    skipping: " + aDriver.getClass().getName());
                }
    
            }

这个增强for循环中有一个registeredDrivers对象,我们通过查看可以知道他是一个ArrayList,存在这个List的对象是DriverInfo,现在先看一下DriverInfo

    final Driver driver;
    DriverAction da;
    DriverInfo(Driver driver, DriverAction action) {
        this.driver = driver;
        da = action;
    }

可以看到他接收了一个成员变量driver,那我们初步可以判断,DriverManager是通过registeredDrivers这个集合来获取已经注册好的驱动,那registeredDrivers这个集合是在什么地方添加对象的呢?

    public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }

根据关键字"registeredDrivers"查找,我们发现了registerDriver这个方法,如果driver对象不为空就尝试进行添加,如果没有添加过,我们就会将该driver对象加入registerDriver集合中,而注册驱动是我们第一步做的事情,所以在我们注册驱动的时候,已经将com.mysql.jdbc.Driver加到了该集合中

再回到增强for循环看他做了什么事

         Connection con = aDriver.driver.connect(url, info);
            if (con != null) {
                // Success!
                println("getConnection returning " + aDriver.driver.getClass().getName());
                return (con);
            }

DriverManger通过每次遍历到的aDriver获取里面的driver对象,再调用它的connect方法,将url(数据库地址),info(访问用户名,访问密码)拿来使用,获取实现了Connection接口的实现类,到此,我们初步可以看到DriverManager是如何获取Connection实现类的了

去到我们注册的com.mysql.jdbc.Driver类中找connect方法,发现没找到,那子类没有的方法一般父类会有,我们继续往父类com.mysql.jdbc.NonRegisteringDriver那查找connect(url,info)方法

我们在父类com.mysql.jdbc.NonRegisteringDriver中可以找到下面方法:

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

在这个方法里我们就能看到它在返回ConnectionImpl对象了

上一篇下一篇

猜你喜欢

热点阅读