1. 程式人生 > >Class.forName("com.mysql.jdbc.Driver") 是個什麼鬼?

Class.forName("com.mysql.jdbc.Driver") 是個什麼鬼?

一句話總結這行程式碼的功能:將mysql驅動註冊到DriverManager中去。

那麼為什麼可以通過這行程式碼實現註冊driver功能呢? 答: 通過呼叫這個方法,就可以將mysql的驅動driver類的class檔案載入到記憶體中; 又由於載入類檔案會執行其中的靜態程式碼塊,此時就會將mysql的driver註冊到系統的DriverManager中。

我們首先看forName方法的原始碼:

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

我們可以看到返回值是 Class<?>,即我們將mysql的驅動類→driver類載入到了 記憶體中。 我們知道,當類被載入到記憶體中時,會首先執行類的靜態程式碼塊中的內容,那我們接下來就看看Driver類的原始碼,看看它被載入到記憶體中時,到底執行了什麼內容。 我們找到com.mysql.jdbc.Driver類:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
        	//1. 新建一個mysql的driver物件
        	//2. 將這個物件註冊到DriverManager中
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

可以看到,靜態程式碼塊中執行的內容是:新建一個msql的driver物件,並將其註冊到DriverManager中去。

接下來我們再看看這個DriverManager.registerDriver 方法,先看這個方法的說明:

  1. 將引數中的driver註冊到DriverManager中。
  2. 新載入的驅動類,應該通過呼叫這個方法來將自己註冊到DriverManager中。
  3. 如果這個驅動已經被註冊,那麼將不會做任何動作。
/**
     * Registers the given driver with the {@code DriverManager}.
     * A newly-loaded driver class should call
     * the method {@code registerDriver} to make itself
     * known to the {@code DriverManager}. If the driver is currently
     * registered, no action is taken.
     * 
     * 
     * 
     */
	public static void registerDriver(java.sql.Driver driver) throws SQLException {
		//它實際呼叫了自身的registerDriver方法
        registerDriver(driver, null);
    }

繼續看這個registerDriver(driver, null);方法

//支援併發的arraylist
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
......
public static 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 中去。這是一個支援併發情況的特殊ArrayList
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }

registeredDrivers.addIfAbsent(new DriverInfo(driver, da)) 表示:如果該驅動尚未註冊,那麼將他新增到 registeredDrivers 中去。從類中的宣告來看這是一個支援併發情況的特殊ArrayList。

此時,Class.forName(“com.mysql.jdbc.Driver”) 的工作就完成了,這一行程式碼的工作就是:將mysql驅動註冊到DriverManager中去

DriverManager.getConnection方法分析

註冊的DriverManager中之後,我們就可以 通過DriverManager的getConnection方法獲得mysql的連線了:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");

接下來我們在看看這個getConnection方法:

	@CallerSensitive
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        ......
        return (getConnection(url, info, Reflection.getCallerClass()));
    }

同樣,呼叫了自身的 getConnection方法;繼續往裡看

private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        ......
        for (DriverInfo aDriver : registeredDrivers) {
			......
            Connection con = aDriver.driver.connect(url, info);
			......
		}
    }

可以看到它對上文提到的特殊ArrayList進行了遍歷,呼叫了driverinfo.driver的connect(url, info); 方法,這是一個介面,由各個不同的驅動自己實現。

    /**
     * Attempts 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 driver manager is asked to connect to a given URL it passes
     * the URL to each loaded driver in turn.
     */
    Connection connect(String url, java.util.Properties info)
        throws SQLException;

到此為止,我們就獲得了connection物件,現在就可以對資料庫進行操作了。