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

4 篇文章 0 订阅
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以及用户名密码等信息,完成连接;

以上,我爱源码!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值