在前一篇中我们分析了驱动的加载,这篇我们看看数据库链接的获取即Connection的获取,废话少说进入正题。

一、获取链接的方式有三种:


1、getConnection(String url,java.util.Properties info);
2、getConnection(String url,String user, String password);
3、getConnection(String url);

三种方式其中我们平时使用jdbc链接的时候用的最多的就是 1和3两种,下面我们首先来看看这三个方法的源码:

二、源码分析

1、getConnection(String url,java.util.Properties info) 方法的源码:

 

public static Connection getConnection(String url,
        java.util.Properties info) throws SQLException {

        // Gets the classloader of the code that called this method, may
        // be null.
        ClassLoader callerCL = DriverManager.getCallerClassLoader();

        return (getConnection(url, info, callerCL));
    }
DriverManager调用方法获取类加载器。然后在调用getConnection(url,info,callerCL)方法获取Connection 链接,其中的<span style="font-family: Arial, Helvetica, sans-serif;">DriverManager.getCallerClassLoader()这个方法是native 方法。</span>



2、getConnection(String url,String user, String password) 方法源码:

 

public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        // Gets the classloader of the code that called this method, may
        // be null.
        ClassLoader callerCL = DriverManager.getCallerClassLoader();

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

        return (getConnection(url, info, callerCL));
    }



同样首先调用方法获取了类加载器,其次将链接数据库的用户名密码添加到  Properties 中。

Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串



3、getConnection(String url) 方法源码:

public static Connection getConnection(String url)
        throws SQLException {

        java.util.Properties info = new java.util.Properties();

        // Gets the classloader of the code that called this method, may
        // be null.
        ClassLoader callerCL = DriverManager.getCallerClassLoader();

        return (getConnection(url, info, callerCL));
    }

通过这三个源码我们可以看到主要的有两个,一个是获取列加载器的 getCallerClassLoader() 方法一个是 getConnection(url, info, callerCL) 获取链接的方法,下面来看看getConnection(url, info, callerCL)  方法的源码:


//  Worker method called by the public getConnection() methods.
	// 实际是通过这个方法来获取connection的
	// 通过之前的方法我们可以看到,所有的方法都调用了这个方法即请求地址,properties 和类加载器
    private static Connection getConnection(
        String url, java.util.Properties info, ClassLoader callerCL) 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.
         */
		 // 同步代码块
        synchronized(DriverManager.class) {
          // synchronize loading of the correct classloader.
          if(callerCL == null) {
	  // 如果类加载器是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;
       // 这里的这个集合就是 刚开始 调用resigerDriver方法将加载的驱动存放到这个集合中
        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
			// 调用isDriverAllowed 这个方法判断是不是同一个类,即Class的对象是不是相等。
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
					// 获取链接,这里调用了Driver的父类的方法。
                    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");
    }



这个方法判断驱动如果驱动类相等则调用Driver的父类进行和数据库的链接如果不相等则抛出异常链接结束。

判断Class 的类型的对象是不是相等的方法源码如下:

private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
        boolean result = false;
        if(driver != null) {
            Class<?> aClass = null;
            try { 
	// 这里正好用到了反射的两个东西我们来看看
	// object.getClass() 获取对象的类型即Class类的对象,通过调用Class类提供的方法可以获取这个类的类名称(包名+类名)以及类加载器。
	//  使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。
                aClass =  Class.forName(driver.getClass().getName(), true, classLoader);
            } catch (Exception ex) {
                result = false;
            }
        //比较两个Class 的实例是不是相等,其实就是判断 是不是同一个类即:包名+类名+加载器名称,只要类名获取类加载器不同就不是同一个类。
             result = ( aClass == driver.getClass() ) ? true : false;
        }

        return result;
    }



ok 到这里该判断的都判断了,该比较的都比较了,现在我们看看 Driver的父类的conect方法,源代码如下:

/**
     * Try to make a database connection to the given URL. The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given
     * URL. This will be common, as when the JDBC driverManager is asked to connect to a given URL, it passes the URL to each loaded driver in turn.
     * 
     * <p>
     * The driver should raise an SQLException if the URL is null or if it is the right driver to connect to the given URL, but has trouble connecting to the
     * database.
     * </p>
     * 
     * <p>
     * The java.util.Properties argument can be used to pass arbitrary string tag/value pairs as connection arguments. These properties take precedence over any
     * properties sent in the URL.
     * </p>
     * 
     * <p>
     * MySQL protocol takes the form:
     * 
     * <PRE>
     * jdbc:mysql://host:port/database
     * </PRE>
     * 
     * </p>
     * 
     * @param url
     *            the URL of the database to connect to
     * @param info
     *            a list of arbitrary tag/value pairs as connection arguments
     * 
     * @return a connection to the URL or null if it isn't us
     * 
     * @exception SQLException
     *                if a database access error occurs or the url is null
     * 
     * @see java.sql.Driver#connect
     */
	 // 获取给定url的数据库的链接,inro 就是用户名和密码等参数,如果url 为null 则会返回一个异常信息。
    public java.sql.Connection connect(String url, Properties info) throws SQLException {
		// url: jdbc:mysql://ip:port/dbname
        if (url == null) {
            throw SQLError.createSQLException(Messages.getString("NonRegisteringDriver.1"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null);
        }
        // 判断url前缀是不是满足url的格式
        if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) {
            return connectLoadBalanced(url, info);
        } else if (StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) {
            return connectReplicationConnection(url, info);
        }

        Properties props = null;
         // parseUrl(url,info)方法解析url ,用户名密码以及其他的参数,并添加到Properties集合中
        if ((props = parseURL(url, info)) == null) {
            return null;
        }

        if (!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))) {
            return connectFailover(url, info);
        }

        try {
			// 这里获取具体的链接 类是ConnectionImpl
			// 其中调用的host port database 方法获取对应的参数的值
            Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(host(props), port(props), props, database(props), url);

            return newConn;
        } catch (SQLException sqlEx) {
            // Don't wrap SQLExceptions, throw
            // them un-changed.
            throw sqlEx;
        } catch (Exception ex) {
            SQLException sqlEx = SQLError.createSQLException(
                    Messages.getString("NonRegisteringDriver.17") + ex.toString() + Messages.getString("NonRegisteringDriver.18"),
                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null);

            sqlEx.initCause(ex);

            throw sqlEx;
        }
    }



我们据需跟进 com.mysql.jdbc.ConnectionImpl.getInstance(host(props), port(props), props, database(props), url);这个方法,源码如下:


/**
     * Creates a connection instance -- We need to provide factory-style methods
     * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise
     * the class verifier complains when it tries to load JDBC4-only interface
     * classes that are present in JDBC4 method signatures.
     */
         // 参数有: 主机  端口号  properties  database url 
    protected static Connection getInstance(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url)
            throws SQLException {
				// 调用util 类的方法判断,驱动类是否能够找到
				// 创建ConnectionImpl 对象。
        if (!Util.isJdbc4()) {
            return new ConnectionImpl(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
        }

        return (Connection) Util.handleNewInstance(JDBC_4_CONNECTION_CTOR, new Object[] { hostToConnectTo, Integer.valueOf(portToConnectTo), info,
                databaseToConnectTo, url }, null);
    }



ConnectionImpl的构造函数来创建一个Connection实例,即向mysql服务获取链接:
<pre name="code" class="java">/**
     * Creates a connection to a MySQL Server.
     * 
     * @param hostToConnectTo
     *            the hostname of the database server
     * @param portToConnectTo
     *            the port number the server is listening on
     * @param info
     *            a Properties[] list holding the user and password
     * @param databaseToConnectTo
     *            the database to connect to
     * @param url
     *            the URL of the connection
     * @param d
     *            the Driver instantation of the connection
     * @exception SQLException
     *                if a database access error occurs
     */
	 // 构造函数来创建链接实例
    public ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLException {

        this.connectionCreationTimeMillis = System.currentTimeMillis();

        if (databaseToConnectTo == null) {
            databaseToConnectTo = "";
        }

        // Stash away for later, used to clone this connection for Statement.cancel and Statement.setQueryTimeout().
        //

        this.origHostToConnectTo = hostToConnectTo;  // host 
        this.origPortToConnectTo = portToConnectTo;  //port
        this.origDatabaseToConnectTo = databaseToConnectTo;  //数据库名

        try {
            Blob.class.getMethod("truncate", new Class[] { Long.TYPE });

            this.isRunningOnJDK13 = false;
        } catch (NoSuchMethodException nsme) {
            this.isRunningOnJDK13 = true;
        }

        this.sessionCalendar = new GregorianCalendar();
        this.utcCalendar = new GregorianCalendar();
        this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));

        //
        // Normally, this code would be in initializeDriverProperties, but we need to do this as early as possible, so we can start logging to the 'correct'
        // place as early as possible...this.log points to 'NullLogger' for every connection at startup to avoid NPEs and the overhead of checking for NULL at
        // every logging call.
        //
        // We will reset this to the configured logger during properties initialization.
        //
        this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor());

        this.openStatements = new HashMap<Statement, Statement>();

        if (NonRegisteringDriver.isHostPropertiesList(hostToConnectTo)) {
            Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(hostToConnectTo);

            Enumeration<?> propertyNames = hostSpecificProps.propertyNames();

            while (propertyNames.hasMoreElements()) {
                String propertyName = propertyNames.nextElement().toString();
                String propertyValue = hostSpecificProps.getProperty(propertyName);

                info.setProperty(propertyName, propertyValue);
            }
        } else {

            if (hostToConnectTo == null) {
                this.host = "localhost";
                this.hostPortPair = this.host + ":" + portToConnectTo;
            } else {
                this.host = hostToConnectTo;

                if (hostToConnectTo.indexOf(":") == -1) {
                    this.hostPortPair = this.host + ":" + portToConnectTo;
                } else {
                    this.hostPortPair = this.host;
                }
            }
        }
       // 获取了所有链接数据库需要的参数
        this.port = portToConnectTo;
        this.database = databaseToConnectTo;
        this.myURL = url;
        this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
        this.password = info.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
        if ((this.user == null) || this.user.equals("")) {
            this.user = "";
        }

        if (this.password == null) {
            this.password = "";
        }

        this.props = info;

        initializeDriverProperties(info);

        // We store this per-connection, due to static synchronization issues in Java's built-in TimeZone class...
        this.defaultTimeZone = TimeUtil.getDefaultTimeZone(getCacheDefaultTimezone());

        this.isClientTzUTC = !this.defaultTimeZone.useDaylightTime() && this.defaultTimeZone.getRawOffset() == 0;

        if (getUseUsageAdvisor()) {
            this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable());
        } else {
            this.pointOfOrigin = "";
        }

        try {
            this.dbmd = getMetaData(false, false);
			// 进行数据库的链接
            initializeSafeStatementInterceptors();
			// 创建io流
            createNewIO(false);
			//
            unSafeStatementInterceptors();
        } catch (SQLException ex) {
            cleanup(ex);

            // don't clobber SQL exceptions
            throw ex;
        } catch (Exception ex) {
            cleanup(ex);

            StringBuilder mesg = new StringBuilder(128);

            if (!getParanoid()) {
                mesg.append("Cannot connect to MySQL server on ");
                mesg.append(this.host);
                mesg.append(":");
                mesg.append(this.port);
                mesg.append(".\n\n");
                mesg.append("Make sure that there is a MySQL server ");
                mesg.append("running on the machine/port you are trying ");
                mesg.append("to connect to and that the machine this software is running on ");
                mesg.append("is able to connect to this host/port (i.e. not firewalled). ");
                mesg.append("Also make sure that the server has not been started with the --skip-networking ");
                mesg.append("flag.\n\n");
            } else {
                mesg.append("Unable to connect to database.");
            }

            SQLException sqlEx = SQLError.createSQLException(mesg.toString(), SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, getExceptionInterceptor());

            sqlEx.initCause(ex);

            throw sqlEx;
        }

        NonRegisteringDriver.trackConnection(this);
    }






我们发现具体的链接没有在这个方法中实现,而是在createNewIO()这个方法中实现的,具体代码如下:

ConnectionImpl 类中的createNewIO() 方法如下:

/**
     * Creates an IO channel to the server
     * 
     * @param isForReconnect
     *            is this request for a re-connect
     * @return a new MysqlIO instance connected to a server
     * @throws SQLException
     *             if a database access error occurs
     * @throws CommunicationsException
     */
	 // 创建io流,在调用这个方法的时候传的参数是false
    public void createNewIO(boolean isForReconnect) throws SQLException {
		// 同步代码块
        synchronized (getConnectionMutex()) {
            // Synchronization Not needed for *new* connections, but defintely for connections going through fail-over, since we might get the new connection up
            // and running *enough* to start sending cached or still-open server-side prepared statements over to the backend before we get a chance to
            // re-prepare them...

            Properties mergedProps = exposeAsProperties(this.props);

            if (!getHighAvailability()) {
                connectOneTryOnly(isForReconnect, mergedProps);

                return;
            }
             // 调用方法尝试多次链接
            connectWithRetries(isForReconnect, mergedProps);
        }
    }

调用ConnectionImpl 类的

connectWithRetries方法进行多次的尝试链接:

// 链接的具体实现
    private void connectWithRetries(boolean isForReconnect, Properties mergedProps) throws SQLException {
        double timeout = getInitialTimeout();
        boolean connectionGood = false;

        Exception connectionException = null;
        // 在没有获的链接的情况下尝试链接多次,知道最大尝试次数
        for (int attemptCount = 0; (attemptCount < getMaxReconnects()) && !connectionGood; attemptCount++) {
            try {
                if (this.io != null) {
                    this.io.forceClose();
                }
                // 调用这个方法进行链接获取
                coreConnect(mergedProps);
                pingInternal(false, 0);

                boolean oldAutoCommit;
                int oldIsolationLevel;
                boolean oldReadOnly;
                String oldCatalog;

                synchronized (getConnectionMutex()) {
                    this.connectionId = this.io.getThreadId();
                    this.isClosed = false;

                    // save state from old connection
                    oldAutoCommit = getAutoCommit();
                    oldIsolationLevel = this.isolationLevel;
                    oldReadOnly = isReadOnly(false);
                    oldCatalog = getCatalog();

                    this.io.setStatementInterceptors(this.statementInterceptors);
                }

                // Server properties might be different from previous connection, so initialize again...
                initializePropsFromServer();

                if (isForReconnect) {
                    // Restore state from old connection
                    setAutoCommit(oldAutoCommit);

                    if (this.hasIsolationLevels) {
                        setTransactionIsolation(oldIsolationLevel);
                    }

                    setCatalog(oldCatalog);
                    setReadOnly(oldReadOnly);
                }

                connectionGood = true;

                break;
            } catch (Exception EEE) {
                connectionException = EEE;
                connectionGood = false;
            }

            if (connectionGood) {
                break;
            }

            if (attemptCount > 0) {
                try {
                    Thread.sleep((long) timeout * 1000);
                } catch (InterruptedException IE) {
                    // ignore
                }
            }
        } // end attempts for a single host

        if (!connectionGood) {
            // We've really failed!
            SQLException chainedEx = SQLError.createSQLException(
                    Messages.getString("Connection.UnableToConnectWithRetries", new Object[] { Integer.valueOf(getMaxReconnects()) }),
                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor());
            chainedEx.initCause(connectionException);

            throw chainedEx;
        }

        if (getParanoid() && !getHighAvailability()) {
            this.password = null;
            this.user = null;
        }

        if (isForReconnect) {
            //
            // Retrieve any 'lost' prepared statements if re-connecting
            //
            Iterator<Statement> statementIter = this.openStatements.values().iterator();

            //
            // We build a list of these outside the map of open statements, because in the process of re-preparing, we might end up having to close a prepared
            // statement, thus removing it from the map, and generating a ConcurrentModificationException
            //
            Stack<Statement> serverPreparedStatements = null;

            while (statementIter.hasNext()) {
                Statement statementObj = statementIter.next();

                if (statementObj instanceof ServerPreparedStatement) {
                    if (serverPreparedStatements == null) {
                        serverPreparedStatements = new Stack<Statement>();
                    }

                    serverPreparedStatements.add(statementObj);
                }
            }

            if (serverPreparedStatements != null) {
                while (!serverPreparedStatements.isEmpty()) {
                    ((ServerPreparedStatement) serverPreparedStatements.pop()).rePrepare();
                }
            }
        }
    }



ConnectionImpl类中获取链接I/O流的核心代码:

// 链接的核心代码
    private void coreConnect(Properties mergedProps) throws SQLException, IOException {
        // 默认端口号
		int newPort = 3306;
		// 默认主机名 localhost
        String newHost = "localhost";
        // 协议
        String protocol = mergedProps.getProperty(NonRegisteringDriver.PROTOCOL_PROPERTY_KEY);

        if (protocol != null) {
            // "new" style URL

            if ("tcp".equalsIgnoreCase(protocol)) {
                newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY));
                newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"));
            } else if ("pipe".equalsIgnoreCase(protocol)) {
                setSocketFactoryClassName(NamedPipeSocketFactory.class.getName());

                String path = mergedProps.getProperty(NonRegisteringDriver.PATH_PROPERTY_KEY);

                if (path != null) {
                    mergedProps.setProperty(NamedPipeSocketFactory.NAMED_PIPE_PROP_NAME, path);
                }
            } else {
                // normalize for all unknown protocols
                newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY));
                newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"));
            }
        } else {

            String[] parsedHostPortPair = NonRegisteringDriver.parseHostPortPair(this.hostPortPair);
            newHost = parsedHostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];

            newHost = normalizeHost(newHost);

            if (parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) {
                newPort = parsePortNumber(parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]);
            }
        }

        this.port = newPort;
        this.host = newHost;

        // reset max-rows to default value
        this.sessionMaxRows = -1;
        // io创建获取和mysql 服务器的链接
		  // 方法getSocketFactoryClassName() 获取类名
	   /** The I/O abstraction interface (network conn to MySQL server */
	   // 获得和mysql服务的链接
        this.io = new MysqlIO(newHost, newPort, mergedProps, getSocketFactoryClassName(), getProxy(), getSocketTimeout(),
                this.largeRowSizeThreshold.getValueAsInt());
        this.io.doHandshake(this.user, this.password, this.database);
        if (versionMeetsMinimum(5, 5, 0)) {
            // error messages are returned according to character_set_results which, at this point, is set from the response packet
            this.errorMessageEncoding = this.io.getEncodingForHandshake();
        }
    }



到这里我们发现创建了一个MysqlIO 类,从这个类中获取了io流,而具体的socket 是调用了socketFactory 这个接口的实现类StandardSocketFactory 这个类的实例的connect 方法获取了一个指定的IP ,Port的socket 链接。


StandardSocketFactory类的connect 方法如下:


/**
     * @see com.mysql.jdbc.SocketFactory#createSocket(Properties)
     */
    public Socket connect(String hostname, int portNumber, Properties props) throws SocketException, IOException {

        if (props != null) {
            this.host = hostname;

            this.port = portNumber;

            String localSocketHostname = props.getProperty("localSocketAddress");
            InetSocketAddress localSockAddr = null;
            if (localSocketHostname != null && localSocketHostname.length() > 0) {
                localSockAddr = new InetSocketAddress(InetAddress.getByName(localSocketHostname), 0);
            }

            String connectTimeoutStr = props.getProperty("connectTimeout");

            int connectTimeout = 0;

            if (connectTimeoutStr != null) {
                try {
                    connectTimeout = Integer.parseInt(connectTimeoutStr);
                } catch (NumberFormatException nfe) {
                    throw new SocketException("Illegal value '" + connectTimeoutStr + "' for connectTimeout");
                }
            }

            if (this.host != null) {
                InetAddress[] possibleAddresses = InetAddress.getAllByName(this.host);

                if (possibleAddresses.length == 0) {
                    throw new SocketException("No addresses for host");
                }

                // save last exception to propagate to caller if connection fails
                SocketException lastException = null;

                // Need to loop through all possible addresses. Name lookup may return multiple addresses including IPv4 and IPv6 addresses. Some versions of
                // MySQL don't listen on the IPv6 address so we try all addresses.
                for (int i = 0; i < possibleAddresses.length; i++) {
                    try {
                        this.rawSocket = createSocket(props);

                        configureSocket(this.rawSocket, props);

                        InetSocketAddress sockAddr = new InetSocketAddress(possibleAddresses[i], this.port);
                        // bind to the local port if not using the ephemeral port
                        if (localSockAddr != null) {
                            this.rawSocket.bind(localSockAddr);
                        }

                        this.rawSocket.connect(sockAddr, getRealTimeout(connectTimeout));

                        break;
                    } catch (SocketException ex) {
                        lastException = ex;
                        resetLoginTimeCountdown();
                        this.rawSocket = null;
                    }
                }

                if (this.rawSocket == null && lastException != null) {
                    throw lastException;
                }

                resetLoginTimeCountdown();

                return this.rawSocket;
            }
        }

        throw new SocketException("Unable to create socket");
    }



到这里发现通过mysql 实现驱动的加载获取 驱动类实例后通过各种判断最终就是获取了指定IP,PORT的socket 的获取。获得Socket链接后我们也就获的了和mysql server的链接。