JDBC那点事儿:(一)Class.forName(“xxxxxxx”):

导读:本篇文章讲解 JDBC那点事儿:(一)Class.forName(“xxxxxxx”):,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

JDBC那点事儿:(一)Class.forName(“xxxxxxx”):

author:马衍硕

since:2020-02-24

这是常见的一个jdbc连接数据库,执行查询的一个操作。

import java.sql.*;
  
public class JdbcDemo {
    public static void main(String[] args) throws Exception{
        String url = "jdbc:mysql://127.0.0.1:3306/alg?characterEncoding=utf8&serverTimezone=UTC";
        String username = "root";
        String password = "199606";
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection(url, username, password);
        String sql = "SELECT * FROM tb_account";
        try(Statement stmt = conn.createStatement()){
            ResultSet rs = stmt.executeQuery(sql);
            if(rs.next()){
                System.out.println(rs.getString("id"));
            }
        }
    }
}

看到这句代码我们会很迷惑

Class.forName("com.mysql.cj.jdbc.Driver");

这句代码干了什么?其工作原理是什么?他有什么作用?

为了更好的了解jdbc连接数据库的机制,我们看一下getConnection()的源码;

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

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

        return (getConnection(url, info, Reflection.getCallerClass()));
    }

    //  Worker method called by the public getConnection() methods.
    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");
    }

仔细阅读源代码,我没找到了关键的for循环:

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());
            }

        }

可以分析出,具体的连接工作,是由registeredDrivers中的driver完成。

Connection con = aDriver.driver.connect(url, info);

那registeredDrivers中的driver是什么时候存在的?

好的,总结一下我们现在的疑问:

1、Class.forName(xxxxx)干了什么事?

2、registeredDrivers中的driver是什么时候存在的?

一、Class.forName(xxxxx)干了什么事?

先看源码:

@CallerSensitive
public static Class<?> forName(String className) throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

private static native Class<?> forName0(String name, boolean initialize,
          ClassLoader loader,
          Class<?> caller)
 throws ClassNotFoundException;

注意:
这是java调用其他地方的接口的一个声明关键字,意思是这个方法不是java实现的,native方法是直接交给c/c++来实现。java只能调用,由操作系统实现。

官方文档:

地址:https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#forName-java.lang.String-

public static Class<?> forName(String className)
                        throws ClassNotFoundException
Returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of the current class.
For example, the following code fragment returns the runtime Class descriptor for the class named java.lang.Thread:

Class t = Class.forName("java.lang.Thread")
A call to forName("X") causes the class named X to be initialized.

Parameters:
className - the fully qualified name of the desired class.
Returns:
the Class object for the class with the specified name.
Throws:
LinkageError - if the linkage fails
ExceptionInInitializerError - if the initialization provoked by this method fails
ClassNotFoundException - if the class cannot be located

翻译:

public static Class<?> forName(String className)
                        throws ClassNotFoundException
返回具有给定字符串名称的与类或接口关联的Class对象。 调用此方法等效于:
Class.forName(className,true,currentLoader)
其中currentLoader表示当前类的定义类加载器。
例如,以下代码片段返回名为java.lang.Thread的类的运行时类描述符:

Class t = Class.forName("java.lang.Thread")
调用forName(“ X”)导致初始化名为X的类。

参数:
className-所需类的完全限定名称。
返回值:
具有指定名称的类的Class对象。
抛出:
LinkageError-如果链接失败
ExceptionInInitializerError-如果此方法引发的初始化失败
ClassNotFoundException-如果无法找到该类

可以分析出:

Class.forName的作用是装载一个类并且对其进行实例化的操作。

既然是实例化,那么我们new一个可以吗?试一下

import java.sql.*;
  
public class JdbcDemo {
    public static void main(String[] args) throws Exception{
        String url = "jdbc:mysql://127.0.0.1:3306/alg?characterEncoding=utf8&serverTimezone=UTC";
        String username = "root";
        String password = "199606";
        //Class.forName("com.mysql.cj.jdbc.Driver");
        new com.mysql.cj.jdbc.Driver();
        Connection conn = DriverManager.getConnection(url, username, password);
        String sql = "SELECT * FROM tb_account";
        try(Statement stmt = conn.createStatement()){
            ResultSet rs = stmt.executeQuery(sql);
            if(rs.next()){
                System.out.println(rs.getString("id"));
            }
        }
    }
}

事实证明,也可以!!!

第一个问题我们已经明白了,Class.forName(String classNamae)是对类进行了装载类/实例化的操作,那么实例化com.mysql.cj.jdbc.Driver 与registeredDrivers是不是有什么神秘的联系?

接下来理所当然的想看看实例化com.mysql.cj.jdbc.Driver的过程中发生了什么?

二、registeredDrivers中的driver是什么时候存在的?

源码:

/*
 * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 2.0, as published by the
 * Free Software Foundation.
 *
 * This program is also distributed with certain software (including but not
 * limited to OpenSSL) that is licensed under separate terms, as designated in a
 * particular file or component or in included license documentation. The
 * authors of MySQL hereby grant you an additional permission to link the
 * program and your derivative works with the separately licensed software that
 * they have included with MySQL.
 *
 * Without limiting anything contained in the foregoing, this file, which is
 * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
 * version 1.0, a copy of which can be found at
 * http://oss.oracle.com/licenses/universal-foss-exception.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

package com.mysql.cj.jdbc;

import java.sql.SQLException;

/**
 * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface
 * 
 * <p>
 * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to
 * connect to the target URL.
 * 
 * <p>
 * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast
 * quantities of supporting code.
 * 
 * <p>
 * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a
 * driver by doing Class.forName("foo.bah.Driver")
 */
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * 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()
    }
}

suprise !!! 仔细看静态代码块,原来在加载类的时候就完成了实例化Driver(),并向registerDriver添加的工作

    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

所有的问题我们都搞清楚了,这里我们来总结一下:

1、Class.forName(“com.mysql.cj.jdbc.Driver”)完成加载Driver的操作,在Driver类静态代码块中,实例化Driver(),添加到registerDriver中;

2、在getConnection()中,使用registerDriver中对应的driver以及用户名密码等信息,完成连接;

以上,我爱源码!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/10346.html

(0)
小半的头像小半

相关推荐

极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!